Merge pull request #19475 from 0Pranav/scheduling-ui-rewrite

feat: Appointment Schedulling
diff --git a/erpnext/config/crm.py b/erpnext/config/crm.py
index 0501784..cf10219 100644
--- a/erpnext/config/crm.py
+++ b/erpnext/config/crm.py
@@ -48,6 +48,11 @@
 				},
 				{
 					"type": "doctype",
+					"name": "Appointment",
+					"description" : _("Helps you manage appointments with your leads"),
+				},
+        {
+          "type": "doctype",
 					"name": "Newsletter",
 					"label": _("Newsletter"),
 				}
diff --git a/erpnext/crm/doctype/appointment/__init__.py b/erpnext/crm/doctype/appointment/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/appointment/__init__.py
diff --git a/erpnext/crm/doctype/appointment/appointment.js b/erpnext/crm/doctype/appointment/appointment.js
new file mode 100644
index 0000000..8888b56
--- /dev/null
+++ b/erpnext/crm/doctype/appointment/appointment.js
@@ -0,0 +1,17 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Appointment', {
+	refresh: function(frm) {
+		if(frm.doc.lead){
+			frm.add_custom_button(frm.doc.lead,()=>{
+				frappe.set_route("Form", "Lead", frm.doc.lead);
+			});
+		}
+		if(frm.doc.calendar_event){
+			frm.add_custom_button(__(frm.doc.calendar_event),()=>{
+				frappe.set_route("Form", "Event", frm.doc.calendar_event);
+			});
+		}
+	}
+});
diff --git a/erpnext/crm/doctype/appointment/appointment.json b/erpnext/crm/doctype/appointment/appointment.json
new file mode 100644
index 0000000..32df8ec
--- /dev/null
+++ b/erpnext/crm/doctype/appointment/appointment.json
@@ -0,0 +1,153 @@
+{
+ "autoname": "format:APMT-{customer_name}-{####}",
+ "creation": "2019-08-27 10:48:27.926283",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "scheduled_time",
+  "status",
+  "customer_details_section",
+  "customer_name",
+  "customer_phone_number",
+  "customer_skype",
+  "customer_email",
+  "col_br_2",
+  "customer_details",
+  "linked_docs_section",
+  "lead",
+  "col_br_3",
+  "calendar_event"
+ ],
+ "fields": [
+  {
+   "fieldname": "customer_details_section",
+   "fieldtype": "Section Break",
+   "label": "Customer Details"
+  },
+  {
+   "fieldname": "customer_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Name",
+   "reqd": 1
+  },
+  {
+   "fieldname": "customer_phone_number",
+   "fieldtype": "Data",
+   "label": "Phone Number"
+  },
+  {
+   "fieldname": "customer_skype",
+   "fieldtype": "Data",
+   "label": "Skype ID"
+  },
+  {
+   "fieldname": "customer_details",
+   "fieldtype": "Long Text",
+   "label": "Details"
+  },
+  {
+   "fieldname": "scheduled_time",
+   "fieldtype": "Datetime",
+   "in_list_view": 1,
+   "label": "Scheduled Time",
+   "reqd": 1
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "label": "Status",
+   "options": "Open\nUnverified\nClosed",
+   "reqd": 1
+  },
+  {
+   "fieldname": "lead",
+   "fieldtype": "Link",
+   "label": "Lead",
+   "options": "Lead"
+  },
+  {
+   "fieldname": "calendar_event",
+   "fieldtype": "Link",
+   "label": "Calendar Event",
+   "options": "Event"
+  },
+  {
+   "fieldname": "col_br_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "customer_email",
+   "fieldtype": "Data",
+   "label": "Email",
+   "reqd": 1
+  },
+  {
+   "fieldname": "linked_docs_section",
+   "fieldtype": "Section Break",
+   "label": "Linked Documents"
+  },
+  {
+   "fieldname": "col_br_3",
+   "fieldtype": "Column Break"
+  }
+ ],
+ "modified": "2019-10-14 15:23:54.630731",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Appointment",
+ "name_case": "UPPER CASE",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Guest",
+   "share": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Sales Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Sales User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py
new file mode 100644
index 0000000..2affba2
--- /dev/null
+++ b/erpnext/crm/doctype/appointment/appointment.py
@@ -0,0 +1,223 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+import urllib
+from collections import Counter
+from datetime import timedelta
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import get_url
+from frappe.utils.verified_command import verify_request, get_signed_params
+
+
+class Appointment(Document):
+
+	def find_lead_by_email(self):
+		lead_list = frappe.get_list(
+			'Lead', filters={'email_id': self.customer_email}, ignore_permissions=True)
+		if lead_list:
+			return lead_list[0].name
+		return None
+
+	def before_insert(self):
+		number_of_appointments_in_same_slot = frappe.db.count(
+			'Appointment', filters={'scheduled_time': self.scheduled_time})
+		number_of_agents = frappe.db.get_single_value('Appointment Booking Settings', 'number_of_agents')
+		if not number_of_agents == 0:
+			if (number_of_appointments_in_same_slot >= number_of_agents):
+				frappe.throw('Time slot is not available')
+		# Link lead
+		if not self.lead:
+			self.lead = self.find_lead_by_email()
+
+	def after_insert(self):
+		if self.lead:
+			# Create Calendar event
+			self.auto_assign()
+			self.create_calendar_event()
+		else:
+			# Set status to unverified
+			self.status = 'Unverified'
+			# Send email to confirm
+			self.send_confirmation_email()
+
+	def send_confirmation_email(self):
+		verify_url = self._get_verify_url()
+		template = 'confirm_appointment'
+		args = {
+			"link":verify_url,
+			"site_url":frappe.utils.get_url(),
+			"full_name":self.customer_name,
+		}
+		frappe.sendmail(recipients=[self.customer_email],
+						template=template,
+						args=args,
+						subject=_('Appointment Confirmation'))
+		if frappe.session.user == "Guest":
+			frappe.msgprint(
+				'Please check your email to confirm the appointment')
+		else :
+			frappe.msgprint(
+				'Appointment was created. But no lead was found. Please check the email to confirm')
+
+	def on_change(self):
+		# Sync Calendar
+		if not self.calendar_event:
+			return
+		cal_event = frappe.get_doc('Event', self.calendar_event)
+		cal_event.starts_on = self.scheduled_time
+		cal_event.save(ignore_permissions=True)
+
+
+	def set_verified(self, email):
+		if not email == self.customer_email:
+			frappe.throw('Email verification failed.')
+		# Create new lead
+		self.create_lead_and_link()
+		# Remove unverified status
+		self.status = 'Open'
+		# Create calender event
+		self.auto_assign()
+		self.create_calendar_event()
+		self.save(ignore_permissions=True)
+		frappe.db.commit()
+
+	def create_lead_and_link(self):
+		# Return if already linked
+		if self.lead:
+			return
+		lead = frappe.get_doc({
+			'doctype': 'Lead',
+			'lead_name': self.customer_name,
+			'email_id': self.customer_email,
+			'notes': self.customer_details,
+			'phone': self.customer_phone_number,
+		})
+		lead.insert(ignore_permissions=True)
+		# Link lead
+		self.lead = lead.name
+
+	def auto_assign(self):
+		from frappe.desk.form.assign_to import add as add_assignemnt
+		existing_assignee = self.get_assignee_from_latest_opportunity()
+		if existing_assignee:
+			# If the latest opportunity is assigned to someone
+			# Assign the appointment to the same
+			add_assignemnt({
+				'doctype': self.doctype,
+				'name': self.name,
+				'assign_to': existing_assignee
+			})
+			return
+		if self._assign:
+			return
+		available_agents = _get_agents_sorted_by_asc_workload(
+			self.scheduled_time.date())
+		for agent in available_agents:
+			if(_check_agent_availability(agent, self.scheduled_time)):
+				agent = agent[0]
+				add_assignemnt({
+					'doctype': self.doctype,
+					'name': self.name,
+					'assign_to': agent
+				})
+			break
+
+	def get_assignee_from_latest_opportunity(self):
+		if not self.lead:
+			return None
+		if not frappe.db.exists('Lead', self.lead):
+			return None
+		opporutnities = frappe.get_list(
+			'Opportunity',
+			filters={
+				'party_name': self.lead,
+			},
+			ignore_permissions=True,
+			order_by='creation desc')
+		if not opporutnities:
+			return None
+		latest_opportunity = frappe.get_doc('Opportunity', opporutnities[0].name )
+		assignee = latest_opportunity._assign
+		if not assignee:
+			return None
+		assignee = frappe.parse_json(assignee)[0]
+		return assignee
+
+	def create_calendar_event(self):
+		if self.calendar_event:
+			return
+		appointment_event = frappe.get_doc({
+			'doctype': 'Event',
+			'subject': ' '.join(['Appointment with', self.customer_name]),
+			'starts_on': self.scheduled_time,
+			'status': 'Open',
+			'type': 'Public',
+			'send_reminder': frappe.db.get_single_value('Appointment Booking Settings', 'email_reminders'),
+			'event_participants': [dict(reference_doctype='Lead', reference_docname=self.lead)]
+		})
+		employee = _get_employee_from_user(self._assign)
+		if employee:
+			appointment_event.append('event_participants', dict(
+				reference_doctype='Employee',
+				reference_docname=employee.name))
+		appointment_event.insert(ignore_permissions=True)
+		self.calendar_event = appointment_event.name
+		self.save(ignore_permissions=True)
+	
+	def _get_verify_url(self):
+		verify_route = '/book-appointment/verify'
+		params = {
+			'email': self.customer_email,
+			'appointment': self.name
+		}
+		return get_url(verify_route + '?' + get_signed_params(params))
+
+
+def _get_agents_sorted_by_asc_workload(date):
+	appointments = frappe.db.get_list('Appointment', fields='*')
+	agent_list = _get_agent_list_as_strings()
+	if not appointments:
+		return agent_list
+	appointment_counter = Counter(agent_list)
+	for appointment in appointments:
+		assigned_to = frappe.parse_json(appointment._assign)
+		if not assigned_to:
+			continue
+		if (assigned_to[0] in agent_list) and appointment.scheduled_time.date() == date:
+			appointment_counter[assigned_to[0]] += 1
+	sorted_agent_list = appointment_counter.most_common()
+	sorted_agent_list.reverse()
+	return sorted_agent_list
+
+
+def _get_agent_list_as_strings():
+	agent_list_as_strings = []
+	agent_list = frappe.get_doc('Appointment Booking Settings').agent_list
+	for agent in agent_list:
+		agent_list_as_strings.append(agent.user)
+	return agent_list_as_strings
+
+
+def _check_agent_availability(agent_email, scheduled_time):
+	appointemnts_at_scheduled_time = frappe.get_list(
+		'Appointment', filters={'scheduled_time': scheduled_time})
+	for appointment in appointemnts_at_scheduled_time:
+		if appointment._assign == agent_email:
+			return False
+	return True
+
+
+def _get_employee_from_user(user):
+	employee_docname = frappe.db.exists(
+		{'doctype': 'Employee', 'user_id': user})
+	if employee_docname:
+		# frappe.db.exists returns a tuple of a tuple
+		return frappe.get_doc('Employee', employee_docname[0][0])
+	return None
+
diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py
new file mode 100644
index 0000000..50c98c5
--- /dev/null
+++ b/erpnext/crm/doctype/appointment/test_appointment.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+import datetime
+
+
+def create_test_lead():
+    test_lead = frappe.db.exists({'doctype': 'Lead', 'lead_name': 'Test Lead'})
+    if test_lead:
+        return frappe.get_doc('Lead', test_lead[0][0])
+    test_lead = frappe.get_doc({
+        'doctype': 'Lead',
+        'lead_name': 'Test Lead',
+        'email_id': 'test@example.com'
+    })
+    test_lead.insert(ignore_permissions=True)
+    return test_lead
+
+
+def create_test_appointments():
+    test_appointment = frappe.db.exists(
+        {'doctype': 'Appointment', 'scheduled_time':datetime.datetime.now(),'email':'test@example.com'})
+    if test_appointment:
+        return frappe.get_doc('Appointment', test_appointment[0][0])
+    test_appointment = frappe.get_doc({
+        'doctype': 'Appointment',
+        'email': 'test@example.com',
+        'status': 'Open',
+        'customer_name': 'Test Lead',
+        'customer_phone_number': '666',
+        'customer_skype': 'test',
+        'customer_email': 'test@example.com',
+        'scheduled_time': datetime.datetime.now()
+    })
+    test_appointment.insert()
+    return test_appointment
+
+
+class TestAppointment(unittest.TestCase):
+    test_appointment = test_lead = None
+
+    def setUp(self):
+        self.test_lead = create_test_lead()
+        self.test_appointment = create_test_appointments()
+
+    def test_calendar_event_created(self):
+        cal_event = frappe.get_doc(
+            'Event', self.test_appointment.calendar_event)
+        self.assertEqual(cal_event.starts_on,
+                         self.test_appointment.scheduled_time)
+
+    def test_lead_linked(self):
+        lead = frappe.get_doc('Lead', self.test_lead.name)
+        self.assertIsNotNone(lead)
diff --git a/erpnext/crm/doctype/appointment_booking_settings/__init__.py b/erpnext/crm/doctype/appointment_booking_settings/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_settings/__init__.py
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js
new file mode 100644
index 0000000..99b8214
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js
@@ -0,0 +1,10 @@
+frappe.ui.form.on('Appointment Booking Settings', 'validate',check_times);
+function check_times(frm) {
+	$.each(frm.doc.availability_of_slots || [], function (i, d) {
+		let from_time = Date.parse('01/01/2019 ' + d.from_time);
+		let to_time = Date.parse('01/01/2019 ' + d.to_time);
+		if (from_time > to_time) {
+			frappe.throw(__(`In row ${i + 1} of Appointment Booking Slots : "To Time" must be later than "From Time"`));
+		}
+	});
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
new file mode 100644
index 0000000..4b26e49
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
@@ -0,0 +1,151 @@
+{
+ "creation": "2019-08-27 10:56:48.309824",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "enable_scheduling",
+  "agent_detail_section",
+  "availability_of_slots",
+  "number_of_agents",
+  "agent_list",
+  "holiday_list",
+  "appointment_details_section",
+  "appointment_duration",
+  "email_reminders",
+  "advance_booking_days",
+  "success_details",
+  "success_redirect_url"
+ ],
+ "fields": [
+  {
+   "fieldname": "availability_of_slots",
+   "fieldtype": "Table",
+   "label": "Availability Of Slots",
+   "options": "Appointment Booking Slots",
+   "reqd": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "number_of_agents",
+   "fieldtype": "Int",
+   "hidden": 1,
+   "in_list_view": 1,
+   "label": "Number of Concurrent Appointments",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "holiday_list",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Holiday List",
+   "options": "Holiday List",
+   "reqd": 1
+  },
+  {
+   "default": "60",
+   "fieldname": "appointment_duration",
+   "fieldtype": "Int",
+   "label": "Appointment Duration (In Minutes)",
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "description": "Notify customer and agent via email on the day of the appointment.",
+   "fieldname": "email_reminders",
+   "fieldtype": "Check",
+   "label": "Notify Via Email"
+  },
+  {
+   "default": "7",
+   "fieldname": "advance_booking_days",
+   "fieldtype": "Int",
+   "label": "Number of days appointments can be booked in advance",
+   "reqd": 1
+  },
+  {
+   "fieldname": "agent_list",
+   "fieldtype": "Table MultiSelect",
+   "label": "Agents",
+   "options": "Assignment Rule User",
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "enable_scheduling",
+   "fieldtype": "Check",
+   "label": "Enable Appointment Scheduling",
+   "reqd": 1
+  },
+  {
+   "fieldname": "agent_detail_section",
+   "fieldtype": "Section Break",
+   "label": "Agent Details"
+  },
+  {
+   "fieldname": "appointment_details_section",
+   "fieldtype": "Section Break",
+   "label": "Appointment Details"
+  },
+  {
+   "fieldname": "success_details",
+   "fieldtype": "Section Break",
+   "label": "Success Settings"
+  },
+  {
+   "description": "Leave blank for home.\nThis is relative to site URL, for example \"about\" will redirect to \"https://yoursitename.com/about\"",
+   "fieldname": "success_redirect_url",
+   "fieldtype": "Data",
+   "label": "Success Redirect URL"
+  }
+ ],
+ "issingle": 1,
+ "modified": "2019-11-26 12:14:17.669366",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Appointment Booking Settings",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "Guest",
+   "share": 1
+  },
+  {
+   "create": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "Sales Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
new file mode 100644
index 0000000..27f14b1
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+import datetime
+from frappe.model.document import Document
+
+
+class AppointmentBookingSettings(Document):
+	agent_list = [] #Hack
+	min_date = '01/01/1970 '
+	format_string = "%d/%m/%Y %H:%M:%S"
+
+	def validate(self):
+		self.validate_availability_of_slots()
+
+	def save(self):
+		self.number_of_agents = len(self.agent_list)
+		super(AppointmentBookingSettings, self).save()
+
+	def validate_availability_of_slots(self):
+		for record in self.availability_of_slots:
+			from_time = datetime.datetime.strptime(
+				self.min_date+record.from_time, self.format_string)
+			to_time = datetime.datetime.strptime(
+				self.min_date+record.to_time, self.format_string)
+			timedelta = to_time-from_time
+			self.validate_from_and_to_time(from_time, to_time)
+			self.duration_is_divisible(from_time, to_time)
+
+	def validate_from_and_to_time(self, from_time, to_time):
+		if from_time > to_time:
+			err_msg = _('<b>From Time</b> cannot be later than <b>To Time</b> for {0}').format(record.day_of_week)
+			frappe.throw(_(err_msg))
+
+	def duration_is_divisible(self, from_time, to_time):
+		timedelta = to_time - from_time
+		if timedelta.total_seconds() % (self.appointment_duration * 60):
+			frappe.throw(
+				_('The difference between from time and To Time must be a multiple of Appointment'))
diff --git a/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py
new file mode 100644
index 0000000..3dc3c39
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestAppointmentBookingSettings(unittest.TestCase):
+	pass
diff --git a/erpnext/crm/doctype/appointment_booking_slots/__init__.py b/erpnext/crm/doctype/appointment_booking_slots/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_slots/__init__.py
diff --git a/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.json b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.json
new file mode 100644
index 0000000..ddf8738
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.json
@@ -0,0 +1,46 @@
+{
+ "creation": "2019-11-19 10:49:49.494927",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "day_of_week",
+  "from_time",
+  "to_time"
+ ],
+ "fields": [
+  {
+   "fieldname": "day_of_week",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Day Of Week",
+   "options": "Sunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday",
+   "reqd": 1
+  },
+  {
+   "fieldname": "from_time",
+   "fieldtype": "Time",
+   "in_list_view": 1,
+   "label": "From Time ",
+   "reqd": 1
+  },
+  {
+   "fieldname": "to_time",
+   "fieldtype": "Time",
+   "in_list_view": 1,
+   "label": "To Time",
+   "reqd": 1
+  }
+ ],
+ "istable": 1,
+ "modified": "2019-11-19 10:49:49.494927",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Appointment Booking Slots",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py
new file mode 100644
index 0000000..3cadbc9
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class AppointmentBookingSlots(Document):
+	pass
diff --git a/erpnext/crm/doctype/availability_of_slots/__init__.py b/erpnext/crm/doctype/availability_of_slots/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/availability_of_slots/__init__.py
diff --git a/erpnext/crm/doctype/availability_of_slots/availability_of_slots.json b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.json
new file mode 100644
index 0000000..b54af8d
--- /dev/null
+++ b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.json
@@ -0,0 +1,46 @@
+{
+ "creation": "2019-09-10 15:02:05.779434",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "day_of_week",
+  "from_time",
+  "to_time"
+ ],
+ "fields": [
+  {
+   "fieldname": "day_of_week",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Day Of Week",
+   "options": "Sunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday",
+   "reqd": 1
+  },
+  {
+   "fieldname": "from_time",
+   "fieldtype": "Time",
+   "in_list_view": 1,
+   "label": "From Time",
+   "reqd": 1
+  },
+  {
+   "fieldname": "to_time",
+   "fieldtype": "Time",
+   "in_list_view": 1,
+   "label": "To Time",
+   "reqd": 1
+  }
+ ],
+ "istable": 1,
+ "modified": "2019-09-10 15:05:20.406855",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Availability Of Slots",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py
new file mode 100644
index 0000000..8258471
--- /dev/null
+++ b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class AvailabilityOfSlots(Document):
+	pass
diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json
index 3c22dc7..eb68c67 100644
--- a/erpnext/crm/doctype/lead/lead.json
+++ b/erpnext/crm/doctype/lead/lead.json
@@ -1,1436 +1,372 @@
 {
- "allow_copy": 0,
  "allow_events_in_timeline": 1,
- "allow_guest_to_view": 0,
  "allow_import": 1,
- "allow_rename": 0,
  "autoname": "naming_series:",
- "beta": 0,
  "creation": "2013-04-10 11:45:37",
- "custom": 0,
- "docstatus": 0,
  "doctype": "DocType",
  "document_type": "Document",
- "editable_grid": 0,
+ "engine": "InnoDB",
+ "field_order": [
+  "organization_lead",
+  "lead_details",
+  "naming_series",
+  "lead_name",
+  "company_name",
+  "email_id",
+  "col_break123",
+  "lead_owner",
+  "status",
+  "gender",
+  "source",
+  "customer",
+  "campaign_name",
+  "image",
+  "section_break_12",
+  "contact_by",
+  "column_break_14",
+  "contact_date",
+  "ends_on",
+  "notes_section",
+  "notes",
+  "contact_info",
+  "address_desc",
+  "address_html",
+  "column_break2",
+  "contact_html",
+  "phone",
+  "salutation",
+  "mobile_no",
+  "fax",
+  "website",
+  "territory",
+  "more_info",
+  "type",
+  "market_segment",
+  "industry",
+  "request_type",
+  "column_break3",
+  "company",
+  "unsubscribed",
+  "blog_subscriber"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "depends_on": "",
-   "fetch_if_empty": 0,
+   "default": "0",
    "fieldname": "organization_lead",
    "fieldtype": "Check",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Lead is an Organization",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 1,
-   "translatable": 0,
-   "unique": 0
+   "set_only_once": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "lead_details",
    "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "",
-   "length": 0,
-   "no_copy": 0,
-   "options": "fa fa-user",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "fa fa-user"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "default": "",
-   "fetch_if_empty": 0,
    "fieldname": "naming_series",
    "fieldtype": "Select",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Series",
-   "length": 0,
    "no_copy": 1,
    "oldfieldname": "naming_series",
    "oldfieldtype": "Select",
    "options": "CRM-LEAD-.YYYY.-",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 1,
-   "translatable": 0,
-   "unique": 0
+   "set_only_once": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:!doc.organization_lead",
-   "fetch_if_empty": 0,
    "fieldname": "lead_name",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
    "in_global_search": 1,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Person Name",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "lead_name",
    "oldfieldtype": "Data",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 1,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "search_index": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "company_name",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
    "in_list_view": 1,
-   "in_standard_filter": 0,
    "label": "Organization Name",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "company_name",
-   "oldfieldtype": "Data",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "oldfieldtype": "Data"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "email_id",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Email Address",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "email_id",
    "oldfieldtype": "Data",
    "options": "Email",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 1,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "search_index": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "col_break123",
    "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0,
    "width": "50%"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "default": "__user",
-   "fetch_if_empty": 0,
    "fieldname": "lead_owner",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
    "in_list_view": 1,
-   "in_standard_filter": 0,
    "label": "Lead Owner",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "lead_owner",
    "oldfieldtype": "Link",
    "options": "User",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 1,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "search_index": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "default": "Lead",
-   "fetch_if_empty": 0,
    "fieldname": "status",
    "fieldtype": "Select",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
    "in_list_view": 1,
    "in_standard_filter": 1,
    "label": "Status",
-   "length": 0,
    "no_copy": 1,
    "oldfieldname": "status",
    "oldfieldtype": "Select",
    "options": "Lead\nOpen\nReplied\nOpportunity\nQuotation\nLost Quotation\nInterested\nConverted\nDo Not Contact",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
    "reqd": 1,
-   "search_index": 1,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "search_index": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:!doc.organization_lead",
-   "fetch_if_empty": 0,
    "fieldname": "gender",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Gender",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Gender",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Gender"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "depends_on": "",
-   "fetch_if_empty": 0,
    "fieldname": "source",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Source",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "source",
    "oldfieldtype": "Select",
-   "options": "Lead Source",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Lead Source"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:doc.source == 'Existing Customer'",
-   "fetch_if_empty": 0,
    "fieldname": "customer",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "From Customer",
-   "length": 0,
    "no_copy": 1,
    "oldfieldname": "customer",
    "oldfieldtype": "Link",
-   "options": "Customer",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Customer"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval: doc.source==\"Campaign\"",
-   "description": "",
-   "fetch_if_empty": 0,
    "fieldname": "campaign_name",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Campaign Name",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "campaign_name",
    "oldfieldtype": "Link",
-   "options": "Campaign",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Campaign"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "image",
    "fieldtype": "Attach Image",
    "hidden": 1,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Image",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 1,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "print_hide": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "section_break_12",
    "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Follow Up",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Follow Up"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "contact_by",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Next Contact By",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "contact_by",
    "oldfieldtype": "Link",
    "options": "User",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0,
    "width": "100px"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "column_break_14",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
    "bold": 1,
-   "collapsible": 0,
-   "columns": 0,
-   "description": "",
-   "fetch_if_empty": 0,
    "fieldname": "contact_date",
    "fieldtype": "Datetime",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Next Contact Date",
-   "length": 0,
    "no_copy": 1,
    "oldfieldname": "contact_date",
    "oldfieldtype": "Date",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0,
    "width": "100px"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
    "bold": 1,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "ends_on",
    "fieldtype": "Datetime",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Ends On",
-   "length": 0,
-   "no_copy": 1,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "no_copy": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
    "collapsible": 1,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "notes_section",
    "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Notes",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Notes"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "notes",
    "fieldtype": "Text Editor",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Notes",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Notes"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
    "collapsible": 1,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "contact_info",
    "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Address & Contact",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldtype": "Column Break",
-   "options": "fa fa-map-marker",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "fa fa-map-marker"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:doc.__islocal",
-   "fetch_if_empty": 0,
    "fieldname": "address_desc",
    "fieldtype": "HTML",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Address Desc",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "print_hide": 1,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "print_hide": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "address_html",
    "fieldtype": "HTML",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Address HTML",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "read_only": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "column_break2",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:doc.organization_lead",
-   "fetch_if_empty": 0,
    "fieldname": "contact_html",
    "fieldtype": "HTML",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Contact HTML",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "read_only": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:!doc.organization_lead",
-   "fetch_if_empty": 0,
    "fieldname": "phone",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Phone",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "contact_no",
-   "oldfieldtype": "Data",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "oldfieldtype": "Data"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:!doc.organization_lead",
-   "fetch_if_empty": 0,
    "fieldname": "salutation",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Salutation",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Salutation",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Salutation"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:!doc.organization_lead",
-   "fetch_if_empty": 0,
    "fieldname": "mobile_no",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Mobile No.",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "mobile_no",
-   "oldfieldtype": "Data",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "oldfieldtype": "Data"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:!doc.organization_lead",
-   "fetch_if_empty": 0,
    "fieldname": "fax",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Fax",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "fax",
-   "oldfieldtype": "Data",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "oldfieldtype": "Data"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "website",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Website",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "website",
-   "oldfieldtype": "Data",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "oldfieldtype": "Data"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "description": "",
-   "fetch_if_empty": 0,
    "fieldname": "territory",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Territory",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "territory",
    "oldfieldtype": "Link",
    "options": "Territory",
-   "permlevel": 0,
-   "print_hide": 1,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "print_hide": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
    "collapsible": 1,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "more_info",
    "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "More Information",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldtype": "Section Break",
-   "options": "fa fa-file-text",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "fa fa-file-text"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "type",
    "fieldtype": "Select",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Lead Type",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "type",
    "oldfieldtype": "Select",
-   "options": "\nClient\nChannel Partner\nConsultant",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "\nClient\nChannel Partner\nConsultant"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "market_segment",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Market Segment",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "market_segment",
    "oldfieldtype": "Select",
-   "options": "Market Segment",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Market Segment"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "industry",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Industry",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "industry",
    "oldfieldtype": "Link",
-   "options": "Industry Type",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Industry Type"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "request_type",
    "fieldtype": "Select",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Request Type",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "request_type",
    "oldfieldtype": "Select",
-   "options": "\nProduct Enquiry\nRequest for Information\nSuggestions\nOther",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "\nProduct Enquiry\nRequest for Information\nSuggestions\nOther"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "column_break3",
    "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
    "oldfieldtype": "Column Break",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0,
    "width": "50%"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "company",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Company",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "company",
    "oldfieldtype": "Link",
    "options": "Company",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 1,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "remember_last_selected_value": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
+   "default": "0",
    "fieldname": "unsubscribed",
    "fieldtype": "Check",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Unsubscribed",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Unsubscribed"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
+   "default": "0",
    "fieldname": "blog_subscriber",
    "fieldtype": "Check",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Blog Subscriber",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Blog Subscriber"
   }
  ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
  "icon": "fa fa-user",
  "idx": 5,
  "image_field": "image",
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-06-18 03:22:57.283628",
+ "modified": "2019-09-19 12:49:02.536647",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Lead",
@@ -1438,128 +374,69 @@
  "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0,
-   "cancel": 0,
-   "create": 0,
-   "delete": 0,
-   "email": 0,
-   "export": 0,
-   "if_owner": 0,
-   "import": 0,
    "permlevel": 1,
-   "print": 0,
    "read": 1,
    "report": 1,
-   "role": "All",
-   "set_user_permissions": 0,
-   "share": 0,
-   "submit": 0,
-   "write": 0
+   "role": "All"
   },
   {
-   "amend": 0,
-   "cancel": 0,
    "create": 1,
-   "delete": 0,
    "email": 1,
-   "export": 0,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
    "role": "Sales User",
-   "set_user_permissions": 0,
    "share": 1,
-   "submit": 0,
    "write": 1
   },
   {
-   "amend": 0,
-   "cancel": 0,
    "create": 1,
    "delete": 1,
    "email": 1,
    "export": 1,
-   "if_owner": 0,
    "import": 1,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
    "role": "Sales Manager",
-   "set_user_permissions": 0,
    "share": 1,
-   "submit": 0,
    "write": 1
   },
   {
-   "amend": 0,
-   "cancel": 0,
    "create": 1,
-   "delete": 0,
    "email": 1,
-   "export": 0,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
    "role": "System Manager",
-   "set_user_permissions": 0,
    "share": 1,
-   "submit": 0,
    "write": 1
   },
   {
-   "amend": 0,
-   "cancel": 0,
-   "create": 0,
-   "delete": 0,
-   "email": 0,
-   "export": 0,
-   "if_owner": 0,
-   "import": 0,
    "permlevel": 1,
-   "print": 0,
    "read": 1,
    "report": 1,
-   "role": "Sales Manager",
-   "set_user_permissions": 0,
-   "share": 0,
-   "submit": 0,
-   "write": 0
+   "role": "Sales Manager"
   },
   {
-   "amend": 0,
-   "cancel": 0,
-   "create": 0,
-   "delete": 0,
-   "email": 0,
-   "export": 0,
-   "if_owner": 0,
-   "import": 0,
    "permlevel": 1,
-   "print": 0,
    "read": 1,
    "report": 1,
-   "role": "Sales User",
-   "set_user_permissions": 0,
-   "share": 0,
-   "submit": 0,
-   "write": 0
+   "role": "Sales User"
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Guest",
+   "share": 1
   }
  ],
- "quick_entry": 0,
- "read_only": 0,
  "search_fields": "lead_name,lead_owner,status",
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
- "title_field": "lead_name",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "title_field": "lead_name"
 }
\ No newline at end of file
diff --git a/erpnext/templates/emails/confirm_appointment.html b/erpnext/templates/emails/confirm_appointment.html
new file mode 100644
index 0000000..6c9b28b
--- /dev/null
+++ b/erpnext/templates/emails/confirm_appointment.html
@@ -0,0 +1,10 @@
+<p>{{_("Dear")}} {{ full_name }}{% if last_name %} {{ last_name}}{% endif %},</p>
+<p>{{_("A new appointment has been created for you with {0}").format(site_url)}}.</p>
+<p>{{_("Click on the link below to verify your email and confirm the appointment")}}.</p>
+
+<p style="margin: 30px 0px;">
+	<a href="{{ link }}" rel="nofollow" style="padding: 8px 20px; background-color: #7575ff; color: #fff; border-radius: 4px; text-decoration: none; line-height: 1; border-bottom: 3px solid rgba(0, 0, 0, 0.2); font-size: 14px; font-weight: 200;">{{ _("Verify Email") }}</a>
+</p>
+
+<br>
+<p style="font-size: 85%;">{{_("You can also copy-paste this link in your browser")}} <a href="{{ link }}">{{ link }}</a></p>
diff --git a/erpnext/www/book-appointment/index.css b/erpnext/www/book-appointment/index.css
new file mode 100644
index 0000000..6c49fde
--- /dev/null
+++ b/erpnext/www/book-appointment/index.css
@@ -0,0 +1,53 @@
+.time-slot {
+    margin-bottom: 2em;
+    margin-left: 0.5em;
+    margin-right: 0.5em;
+    border-radius: 0.4em;
+    cursor: pointer;
+    border: 0.5px solid #cccccc;
+    min-height: 75px;
+    padding: 0.5em 1em;
+}
+
+@media (max-width: 768px) {
+    #submit-button-area {
+        display: grid;
+        grid-template-areas: 
+        "submit"
+        "back";
+    }
+}
+#customer-form{
+    border-color: black;
+}
+#customer-form ::placeholder{
+    color: #ddd;
+}
+#timeslot-container{
+	display: flex;
+    flex-wrap: wrap;
+    justify-content: center;
+}
+
+.time-slot:hover {
+    background: #ddd;
+}
+
+.time-slot.unavailable {
+    background: #CBD5E0;
+    cursor: not-allowed;
+    color: #718096
+}
+
+.time-slot.unavailable .text-muted {
+    color: #718096
+}
+
+.time-slot.selected {
+    color: white;
+    background: #5e64ff;
+}
+
+.time-slot.selected .text-muted {
+    color: #EDF2F7 !important;
+}
diff --git a/erpnext/www/book-appointment/index.html b/erpnext/www/book-appointment/index.html
new file mode 100644
index 0000000..96774d5
--- /dev/null
+++ b/erpnext/www/book-appointment/index.html
@@ -0,0 +1,66 @@
+{% extends "templates/web.html" %}
+
+{% block title %}{{ _("Book Appointment") }}{% endblock %}
+
+{% block script %}
+<script src="assets/js/moment-bundle.min.js"></script>
+<script src="book-appointment/index.js"></script>
+{% endblock %}
+
+{% block page_content %}
+<div class="container">
+    <!-- title: Book an appointment -->
+    <div id="select-date-time">
+        <div class="text-center mt-5">
+            <h3>Book an appointment</h3>
+            <p class="lead text-muted" id="lead-text">Select the date and your timezone</p>
+        </div>
+        <div class="row justify-content-center mt-3">
+            <div class="col-md-6 align-self-center ">
+                <div class="row">
+                    <input type="date" oninput="on_date_or_timezone_select()" name="appointment-date"
+                        id="appointment-date" class="form-control mt-3 col-md m-3">
+                    <select name="appointment-timezone" oninput="on_date_or_timezone_select()" id="appointment-timezone"
+                        class="form-control m-3 col-md">
+                    </select>
+                </div>
+            </div>
+        </div>
+        <div class="mt-3" id="timeslot-container">
+
+        </div>
+        <div class="row justify-content-center mt-3">
+            <div class="col-md-4 mb-3">
+                <button class="btn btn-primary form-control" id="next-button">Next</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--Enter Details-->
+<div id="enter-details" class="mb-5">
+    <div class="text-center mt-5">
+        <h3>Add details</h3>
+        <p class="lead text-muted">Selected date is <span class="date-span"></span> at <span class="time-span">
+            </span></p>
+    </div>
+    <div class="row justify-content-center mt-3">
+        <div class="col-md-4 align-items-center">
+            <form id="customer-form" action='#'>
+            <input class="form-control mt-3" type="text" name="customer_name" id="customer_name" placeholder="Your Name (required)" required>
+            <input class="form-control mt-3" type="tel" name="customer_number" id="customer_number" placeholder="+910000000000">
+            <input class="form-control mt-3" type="text" name="customer_skype" id="customer_skype" placeholder="Skype">
+            <input class="form-control mt-3"type="email" name="customer_email" id="customer_email" placeholder="Email Address (required)" required>
+
+            <textarea class="form-control mt-3" name="customer_notes" id="customer_notes" cols="30" rows="10"
+                placeholder="Notes"></textarea>
+            </form>
+            <div class="row mt-3 " id="submit-button-area">
+                <div class="col-md mt-3" style="grid-area: back;"><button class="btn btn-dark form-control" onclick="initialise_select_date()">Go back</button></div>
+                <div class="col-md mt-3" style="grid-area: submit;"><button class="btn btn-primary form-control " onclick="submit()" id="submit-button">Submit</button></div>
+            </div>
+        </div>
+    </div>
+</div>
+</div>
+
+{% endblock %}
\ No newline at end of file
diff --git a/erpnext/www/book-appointment/index.js b/erpnext/www/book-appointment/index.js
new file mode 100644
index 0000000..13c87dd
--- /dev/null
+++ b/erpnext/www/book-appointment/index.js
@@ -0,0 +1,236 @@
+frappe.ready(async () => {
+    initialise_select_date();
+})
+
+window.holiday_list = [];
+
+async function initialise_select_date() {
+    navigate_to_page(1);
+    await get_global_variables();
+    setup_date_picker();
+    setup_timezone_selector();
+    hide_next_button();
+}
+
+async function get_global_variables() {
+    // Using await through this file instead of then.
+    window.appointment_settings = (await frappe.call({
+        method: 'erpnext.www.book-appointment.index.get_appointment_settings'
+    })).message;
+    window.timezones = (await frappe.call({
+        method:'erpnext.www.book-appointment.index.get_timezones'
+    })).message;
+    window.holiday_list = window.appointment_settings.holiday_list;
+}
+
+function setup_timezone_selector() {
+    /**
+     * window.timezones is a dictionary with the following structure
+     * { IANA name: Pretty name}
+     * For example : { Asia/Kolkata : "India Time - Asia/Kolkata"}
+     */
+    let timezones_element = document.getElementById('appointment-timezone');
+    let offset = new Date().getTimezoneOffset();
+    Object.keys(window.timezones).forEach((timezone) => {
+        let opt = document.createElement('option');
+        opt.value = timezone;
+        if (timezone == moment.tz.guess()) {
+            opt.selected = true;
+        }
+        opt.innerHTML = window.timezones[timezone]
+        timezones_element.appendChild(opt)
+    });
+}
+
+function setup_date_picker() {
+    let date_picker = document.getElementById('appointment-date');
+    let today = new Date();
+    date_picker.min = today.toISOString().substr(0, 10);
+    today.setDate(today.getDate() + window.appointment_settings.advance_booking_days);
+    date_picker.max = today.toISOString().substr(0, 10);
+}
+
+function hide_next_button() {
+    let next_button = document.getElementById('next-button');
+    next_button.disabled = true;
+    next_button.onclick = () => frappe.msgprint("Please select a date and time");
+}
+
+function show_next_button() {
+    let next_button = document.getElementById('next-button');
+    next_button.disabled = false;
+    next_button.onclick = setup_details_page;
+}
+
+function on_date_or_timezone_select() {
+    let date_picker = document.getElementById('appointment-date');
+    let timezone = document.getElementById('appointment-timezone');
+    if (date_picker.value === '') {
+        clear_time_slots();
+        hide_next_button();
+        frappe.throw('Please select a date');
+    }
+    window.selected_date = date_picker.value;
+    window.selected_timezone = timezone.value;
+    update_time_slots(date_picker.value, timezone.value);
+    let lead_text = document.getElementById('lead-text');
+    lead_text.innerHTML = "Select Time"
+}
+
+async function get_time_slots(date, timezone) {
+    let slots = (await frappe.call({
+        method: 'erpnext.www.book-appointment.index.get_appointment_slots',
+        args: {
+            date: date,
+            timezone: timezone
+        }
+    })).message;
+    return slots;
+}
+
+async function update_time_slots(selected_date, selected_timezone) {
+    let timeslot_container = document.getElementById('timeslot-container');
+    window.slots = await get_time_slots(selected_date, selected_timezone);
+    clear_time_slots();
+    if (window.slots.length <= 0) {
+        let message_div = document.createElement('p');
+        message_div.innerHTML = "There are no slots available on this date";
+        timeslot_container.appendChild(message_div);
+        return
+    }
+    window.slots.forEach((slot, index) => {
+        // Get and append timeslot div
+        let timeslot_div = get_timeslot_div_layout(slot)
+        timeslot_container.appendChild(timeslot_div);
+    });
+    set_default_timeslot();
+}
+
+function get_timeslot_div_layout(timeslot) {
+    let start_time = new Date(timeslot.time)
+    let timeslot_div = document.createElement('div');
+    timeslot_div.classList.add('time-slot');
+    if (!timeslot.availability) {
+        timeslot_div.classList.add('unavailable')
+    }
+    timeslot_div.innerHTML = get_slot_layout(start_time);
+    timeslot_div.id = timeslot.time.substr(11, 20);
+    timeslot_div.addEventListener('click', select_time);
+    return timeslot_div
+}
+
+function clear_time_slots() {
+    // Clear any existing divs in timeslot container
+    let timeslot_container = document.getElementById('timeslot-container');
+    while (timeslot_container.firstChild) {
+        timeslot_container.removeChild(timeslot_container.firstChild);
+    }
+}
+
+function get_slot_layout(time) {
+    let timezone = document.getElementById("appointment-timezone").value;
+    time = new Date(time);
+    let start_time_string = moment(time).tz(timezone).format("LT");
+    let end_time = moment(time).tz(timezone).add(window.appointment_settings.appointment_duration, 'minutes');
+    let end_time_string = end_time.format("LT");
+    return `<span style="font-size: 1.2em;">${start_time_string}</span><br><span class="text-muted small">to ${end_time_string}</span>`;
+}
+
+function select_time() {
+    if (this.classList.contains('unavailable')) {
+        return;
+    }
+    let selected_element = document.getElementsByClassName('selected');
+    if (!(selected_element.length > 0)) {
+        this.classList.add('selected');
+        show_next_button();
+        return;
+    }
+    selected_element = selected_element[0]
+    window.selected_time = this.id;
+    selected_element.classList.remove('selected');
+    this.classList.add('selected');
+    show_next_button();
+}
+
+function set_default_timeslot() {
+    let timeslots = document.getElementsByClassName('time-slot')
+    // Can't use a forEach here since, we need to break the loop after a timeslot is selected
+    for (let i = 0; i < timeslots.length; i++) {
+        const timeslot = timeslots[i];
+        if (!timeslot.classList.contains('unavailable')) {
+            timeslot.classList.add('selected');
+            break;
+        }
+    }
+}
+
+function navigate_to_page(page_number) {
+    let page1 = document.getElementById('select-date-time');
+    let page2 = document.getElementById('enter-details');
+    switch (page_number) {
+        case 1:
+            page1.style.display = 'block';
+            page2.style.display = 'none';
+            break;
+        case 2:
+            page1.style.display = 'none';
+            page2.style.display = 'block';
+            break;
+        default:
+            break;
+    }
+}
+
+function setup_details_page() {
+    navigate_to_page(2)
+    let date_container = document.getElementsByClassName('date-span')[0];
+    let time_container = document.getElementsByClassName('time-span')[0];
+    date_container.innerHTML = moment(window.selected_date).format("MMM Do YYYY");
+    time_container.innerHTML = moment(window.selected_time, "HH:mm:ss").format("LT");
+}
+
+async function submit() {
+    let button = document.getElementById('submit-button');
+    button.disabled = true;
+    let form = document.querySelector('#customer-form');
+    if (!form.checkValidity()) {
+        form.reportValidity();
+        button.disabled = false;
+        return;
+    }
+    let contact = get_form_data();
+    let appointment =  frappe.call({
+        method: 'erpnext.www.book-appointment.index.create_appointment',
+        args: {
+            'date': window.selected_date,
+            'time': window.selected_time,
+            'contact': contact,
+            'tz':window.selected_timezone
+        },
+        callback: (response)=>{
+            if (response.message.status == "Unverified") {
+                frappe.show_alert("Please check your email to confirm the appointment")
+            } else {
+                frappe.show_alert("Appointment Created Successfully");
+            }
+            setTimeout(()=>{
+                let redirect_url = "/";
+                if (window.appointment_settings.success_redirect_url){
+                    redirect_url += window.appointment_settings.success_redirect_url;
+                }
+                window.location.href = redirect_url;},5000)
+        },
+        error: (err)=>{
+            frappe.show_alert("Something went wrong please try again");
+            button.disabled = false;
+        }
+    });
+}
+
+function get_form_data() {
+    contact = {};
+    let inputs = ['name', 'skype', 'number', 'notes', 'email'];
+    inputs.forEach((id) => contact[id] = document.getElementById(`customer_${id}`).value)
+    return contact
+}
diff --git a/erpnext/www/book-appointment/index.py b/erpnext/www/book-appointment/index.py
new file mode 100644
index 0000000..5b60dd5
--- /dev/null
+++ b/erpnext/www/book-appointment/index.py
@@ -0,0 +1,159 @@
+import frappe
+import datetime
+import json
+import pytz
+
+
+WEEKDAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
+
+no_cache = 1
+
+
+def get_context(context):
+	is_enabled = frappe.db.get_single_value('Appointment Booking Settings', 'enable_scheduling')
+	if is_enabled:
+		return context
+	else:
+		frappe.local.flags.redirect_location = '/404'
+		raise frappe.Redirect
+
+@frappe.whitelist(allow_guest=True)
+def get_appointment_settings():
+	settings = frappe.get_doc('Appointment Booking Settings')
+	settings.holiday_list = frappe.get_doc('Holiday List', settings.holiday_list)
+	return settings
+
+@frappe.whitelist(allow_guest=True)
+def get_timezones():
+	from babel.dates import get_timezone, get_timezone_name, Locale
+	from frappe.utils.momentjs import get_all_timezones
+
+	translated_dict = {}
+	locale = Locale.parse(frappe.local.lang, sep="-")
+
+	for tz in get_all_timezones():
+		timezone_name = get_timezone_name(get_timezone(tz), locale=locale, width='short')
+		if timezone_name:
+			translated_dict[tz] = timezone_name + ' - ' + tz
+
+	return translated_dict
+
+@frappe.whitelist(allow_guest=True)
+def get_appointment_slots(date, timezone):
+	# Convert query to local timezones
+	format_string = '%Y-%m-%d %H:%M:%S'
+	query_start_time = datetime.datetime.strptime(date + ' 00:00:00', format_string)
+	query_end_time = datetime.datetime.strptime(date + ' 23:59:59', format_string)
+	query_start_time = convert_to_system_timezone(timezone, query_start_time)
+	query_end_time = convert_to_system_timezone(timezone, query_end_time)
+	now = convert_to_guest_timezone(timezone, datetime.datetime.now())
+
+	# Database queries
+	settings = frappe.get_doc('Appointment Booking Settings')
+	holiday_list = frappe.get_doc('Holiday List', settings.holiday_list)
+	timeslots = get_available_slots_between(query_start_time, query_end_time, settings)
+
+	# Filter and convert timeslots
+	converted_timeslots = []
+	for timeslot in timeslots:
+		converted_timeslot = convert_to_guest_timezone(timezone, timeslot)
+		# Check if holiday
+		if _is_holiday(converted_timeslot.date(), holiday_list):
+			converted_timeslots.append(dict(time=converted_timeslot, availability=False))
+			continue
+		# Check availability
+		if check_availabilty(timeslot, settings) and converted_timeslot >= now:
+			converted_timeslots.append(dict(time=converted_timeslot, availability=True))
+		else:
+			converted_timeslots.append(dict(time=converted_timeslot, availability=False))
+	date_required = datetime.datetime.strptime(date + ' 00:00:00', format_string).date()
+	converted_timeslots = filter_timeslots(date_required, converted_timeslots)
+	return converted_timeslots
+
+def get_available_slots_between(query_start_time, query_end_time, settings):
+	records = _get_records(query_start_time, query_end_time, settings)
+	timeslots = []
+	appointment_duration = datetime.timedelta(
+		minutes=settings.appointment_duration)
+	for record in records:
+		if record.day_of_week == WEEKDAYS[query_start_time.weekday()]:
+			current_time = _deltatime_to_datetime(query_start_time, record.from_time)
+			end_time = _deltatime_to_datetime(query_start_time, record.to_time)
+		else:
+			current_time = _deltatime_to_datetime(query_end_time, record.from_time)
+			end_time = _deltatime_to_datetime(query_end_time, record.to_time)
+		while current_time + appointment_duration <= end_time:
+			timeslots.append(current_time)
+			current_time += appointment_duration
+	return timeslots
+
+
+@frappe.whitelist(allow_guest=True)
+def create_appointment(date, time, tz, contact):
+	format_string = '%Y-%m-%d %H:%M:%S%z'
+	scheduled_time = datetime.datetime.strptime(date + " " + time, format_string)
+	# Strip tzinfo from datetime objects since it's handled by the doctype
+	scheduled_time = scheduled_time.replace(tzinfo = None)
+	scheduled_time = convert_to_system_timezone(tz, scheduled_time)
+	scheduled_time = scheduled_time.replace(tzinfo = None)
+	# Create a appointment document from form
+	appointment = frappe.new_doc('Appointment')
+	appointment.scheduled_time = scheduled_time
+	contact = json.loads(contact)
+	appointment.customer_name = contact.get('name', None)
+	appointment.customer_phone_number = contact.get('number', None)
+	appointment.customer_skype = contact.get('skype', None)
+	appointment.customer_details = contact.get('notes', None)
+	appointment.customer_email = contact.get('email', None)
+	appointment.status = 'Open'
+	appointment.insert()
+	return appointment
+
+# Helper Functions
+def filter_timeslots(date, timeslots):
+	filtered_timeslots = []
+	for timeslot in timeslots:
+		if(timeslot['time'].date() == date):
+			filtered_timeslots.append(timeslot)
+	return filtered_timeslots
+
+def convert_to_guest_timezone(guest_tz, datetimeobject):
+	guest_tz = pytz.timezone(guest_tz)
+	local_timezone = pytz.timezone(frappe.utils.get_time_zone())
+	datetimeobject = local_timezone.localize(datetimeobject)
+	datetimeobject = datetimeobject.astimezone(guest_tz)
+	return datetimeobject
+
+def convert_to_system_timezone(guest_tz,datetimeobject):
+	guest_tz = pytz.timezone(guest_tz)
+	datetimeobject = guest_tz.localize(datetimeobject)
+	system_tz = pytz.timezone(frappe.utils.get_time_zone())
+	datetimeobject = datetimeobject.astimezone(system_tz)
+	return datetimeobject
+
+def check_availabilty(timeslot, settings):
+	return frappe.db.count('Appointment', {'scheduled_time': timeslot}) < settings.number_of_agents
+
+def _is_holiday(date, holiday_list):
+	for holiday in holiday_list.holidays:
+		if holiday.holiday_date == date:
+			return True
+	return False
+
+
+def _get_records(start_time, end_time, settings):
+	records = []
+	for record in settings.availability_of_slots:
+		if record.day_of_week == WEEKDAYS[start_time.weekday()] or record.day_of_week == WEEKDAYS[end_time.weekday()]:
+			records.append(record)
+	return records
+
+
+def _deltatime_to_datetime(date, deltatime):
+	time = (datetime.datetime.min + deltatime).time()
+	return datetime.datetime.combine(date.date(), time)
+
+
+def _datetime_to_deltatime(date_time):
+	midnight = datetime.datetime.combine(date_time.date(), datetime.time.min)
+	return (date_time-midnight)
\ No newline at end of file
diff --git a/erpnext/www/book-appointment/verify/index.html b/erpnext/www/book-appointment/verify/index.html
new file mode 100644
index 0000000..ebb65b1
--- /dev/null
+++ b/erpnext/www/book-appointment/verify/index.html
@@ -0,0 +1,18 @@
+{% extends "templates/web.html" %}
+
+{% block title %}
+{{ _("Verify Email") }}
+{% endblock%}
+    
+{% block page_content %}
+
+    {% if success==True %}
+        <div class="alert alert-success">
+            Your email has been verified and your appointment has been scheduled
+        </div>
+    {% else %}
+        <div class="alert alert-danger">
+            Verification failed please check the link
+        </div>
+    {% endif %}
+{% endblock%}
\ No newline at end of file
diff --git a/erpnext/www/book-appointment/verify/index.py b/erpnext/www/book-appointment/verify/index.py
new file mode 100644
index 0000000..d4478ae
--- /dev/null
+++ b/erpnext/www/book-appointment/verify/index.py
@@ -0,0 +1,20 @@
+import frappe
+
+from frappe.utils.verified_command import verify_request
+@frappe.whitelist(allow_guest=True)
+def get_context(context):
+	if not verify_request():
+		context.success = False
+		return context
+
+	email = frappe.form_dict['email']
+	appointment_name = frappe.form_dict['appointment']
+
+	if email and appointment_name:
+		appointment = frappe.get_doc('Appointment',appointment_name)
+		appointment.set_verified(email)
+		context.success = True
+		return context
+	else:
+		context.success = False
+		return context
\ No newline at end of file