Merge branch 'develop' of github.com:frappe/erpnext into call-summary-dialog
diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py
new file mode 100644
index 0000000..3bd9246
--- /dev/null
+++ b/erpnext/crm/doctype/utils.py
@@ -0,0 +1,20 @@
+import frappe
+
+def get_document_with_phone_number(number):
+	# finds contacts and leads
+	number = number[-10:]
+	number_filter = {
+		'phone': ['like', '%{}'.format(number)],
+		'mobile_no': ['like', '%{}'.format(number)]
+	}
+	contacts = frappe.get_all('Contact', or_filters=number_filter,
+		fields=['name'], limit=1)
+
+	if contacts:
+		return frappe.get_doc('Contact', contacts[0].name)
+
+	leads = frappe.get_all('Leads', or_filters=number_filter,
+		fields=['name'], limit=1)
+
+	if leads:
+		return frappe.get_doc('Lead', leads[0].name)
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py b/erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.js b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.js
new file mode 100644
index 0000000..bfed491
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Exotel Settings', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json
new file mode 100644
index 0000000..72f47b5
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json
@@ -0,0 +1,61 @@
+{
+ "creation": "2019-05-21 07:41:53.536536",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+  "enabled",
+  "section_break_2",
+  "account_sid",
+  "api_key",
+  "api_token"
+ ],
+ "fields": [
+  {
+   "fieldname": "enabled",
+   "fieldtype": "Check",
+   "label": "Enabled"
+  },
+  {
+   "depends_on": "enabled",
+   "fieldname": "section_break_2",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "account_sid",
+   "fieldtype": "Data",
+   "label": "Account SID"
+  },
+  {
+   "fieldname": "api_token",
+   "fieldtype": "Data",
+   "label": "API Token"
+  },
+  {
+   "fieldname": "api_key",
+   "fieldtype": "Data",
+   "label": "API Key"
+  }
+ ],
+ "issingle": 1,
+ "modified": "2019-05-22 06:25:18.026997",
+ "modified_by": "Administrator",
+ "module": "ERPNext Integrations",
+ "name": "Exotel Settings",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "ASC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
new file mode 100644
index 0000000..77de84c
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
@@ -0,0 +1,21 @@
+# -*- 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
+import requests
+import frappe
+from frappe import _
+
+class ExotelSettings(Document):
+	def validate(self):
+		self.verify_credentials()
+
+	def verify_credentials(self):
+		if self.enabled:
+			response = requests.get('https://api.exotel.com/v1/Accounts/{sid}'
+				.format(sid = self.account_sid), auth=(self.api_key, self.api_token))
+			if response.status_code != 200:
+				frappe.throw(_("Invalid credentials"))
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/test_exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/test_exotel_settings.py
new file mode 100644
index 0000000..5d85615
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/test_exotel_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 TestExotelSettings(unittest.TestCase):
+	pass
diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py
new file mode 100644
index 0000000..3a922f7
--- /dev/null
+++ b/erpnext/erpnext_integrations/exotel_integration.py
@@ -0,0 +1,80 @@
+import frappe
+from erpnext.crm.doctype.utils import get_document_with_phone_number
+import requests
+
+# api/method/erpnext.erpnext_integrations.exotel_integration.handle_incoming_call
+
+@frappe.whitelist(allow_guest=True)
+def handle_incoming_call(*args, **kwargs):
+	# incoming_phone_number = kwargs.get('CallFrom')
+
+	# contact = get_document_with_phone_number(incoming_phone_number)
+	# last_communication = get_last_communication(incoming_phone_number, contact)
+	call_log = create_call_log(kwargs)
+	data = frappe._dict({
+		'call_from': kwargs.get('CallFrom'),
+		'agent_email': kwargs.get('AgentEmail'),
+		'call_type': kwargs.get('Direction'),
+		'call_log': call_log
+	})
+
+	frappe.publish_realtime('show_call_popup', data, user=data.agent_email)
+
+
+def get_last_communication(phone_number, contact):
+	# frappe.get_all('Communication', filter={})
+	return {}
+
+def create_call_log(call_payload):
+	communication = frappe.get_all('Communication', {
+		'communication_medium': 'Phone',
+		'call_id': call_payload.get('CallSid'),
+	}, limit=1)
+
+	if communication:
+		log = frappe.get_doc('Communication', communication[0].name)
+		log.call_status = 'Connected'
+		log.save(ignore_permissions=True)
+		return log
+
+	communication = frappe.new_doc('Communication')
+	communication.subject = frappe._('Call from {}').format(call_payload.get("CallFrom"))
+	communication.communication_medium = 'Phone'
+	communication.send_email = 0
+	communication.phone_no = call_payload.get("CallFrom")
+	communication.comment_type = 'Info'
+	communication.communication_type = 'Communication'
+	communication.status = 'Open'
+	communication.sent_or_received = 'Received'
+	communication.content = 'call_payload'
+	communication.call_status = 'Incoming'
+	communication.communication_date = call_payload.get('StartTime')
+	communication.call_id = call_payload.get('CallSid')
+	communication.save(ignore_permissions=True)
+	return communication
+
+def get_call_status(call_id):
+	settings = get_exotel_settings()
+	response = requests.get('https://{api_key}:{api_token}@api.exotel.com/v1/Accounts/erpnext/{sid}/{call_id}.json'.format(
+		api_key=settings.api_key,
+		api_token=settings.api_token,
+		call_id=call_id
+	))
+	return response.json()
+
+@frappe.whitelist(allow_guest=True)
+def make_a_call(from_number, to_number, caller_id):
+	settings = get_exotel_settings()
+	response = requests.post('https://{api_key}:{api_token}@api.exotel.com/v1/Accounts/{sid}/Calls/connect.json'.format(
+		api_key=settings.api_key,
+		api_token=settings.api_token,
+	), data={
+		'From': from_number,
+		'To': to_number,
+		'CallerId': caller_id
+	})
+
+	return response.json()
+
+def get_exotel_settings():
+	return frappe.get_single('Exotel Settings')
\ No newline at end of file
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 45de6eb..818f336 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -1,7 +1,8 @@
 {
     "css/erpnext.css": [
         "public/less/erpnext.less",
-        "public/less/hub.less"
+        "public/less/hub.less",
+        "public/less/call_popup.less"
     ],
     "css/marketplace.css": [
         "public/less/hub.less"
@@ -48,7 +49,8 @@
 		"public/js/utils/customer_quick_entry.js",
         "public/js/education/student_button.html",
         "public/js/education/assessment_result_tool.html",
-        "public/js/hub/hub_factory.js"
+        "public/js/hub/hub_factory.js",
+        "public/js/call_popup/call_popup.js"
     ],
     "js/item-dashboard.min.js": [
         "stock/dashboard/item_dashboard.html",
diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js
new file mode 100644
index 0000000..2d95c5d
--- /dev/null
+++ b/erpnext/public/js/call_popup/call_popup.js
@@ -0,0 +1,97 @@
+class CallPopup {
+	constructor({ call_from, call_log }) {
+		this.number = call_from;
+		this.call_log = call_log;
+		this.make();
+	}
+
+	make() {
+		this.dialog = new frappe.ui.Dialog({
+			'static': true,
+			'minimizable': true,
+			'fields': [{
+				'fieldname': 'customer_info',
+				'fieldtype': 'HTML'
+			}, {
+				'fieldtype': 'Section Break'
+			}, {
+				'fieldtype': 'Small Text',
+				'label': "Last Communication",
+				'fieldname': 'last_communication',
+				'read_only': true
+			}, {
+				'fieldtype': 'Column Break'
+			}, {
+				'fieldtype': 'Small Text',
+				'label': 'Call Summary',
+				'fieldname': 'call_communication',
+			}, {
+				'fieldtype': 'Button',
+				'label': 'Submit',
+				'click': () => {
+					this.dialog.get_value();
+				}
+			}]
+		});
+		this.set_call_status(this.call_log.call_status);
+		this.make_customer_contact();
+		this.dialog.show();
+		this.dialog.get_close_btn().show();
+		this.dialog.header.find('.indicator').removeClass('hidden').addClass('blue');
+	}
+
+	make_customer_contact() {
+		const wrapper = this.dialog.fields_dict["customer_info"].$wrapper;
+		const contact = this.contact;
+		const customer = this.contact.links ? this.contact.links[0] : null;
+		const customer_link = customer ? frappe.utils.get_form_link(customer.link_doctype, customer.link_name, true): '';
+		if (!contact) {
+			wrapper.append('<b>Unknown Contact</b>');
+		} else {
+			wrapper.append(`
+				<div class="customer-info flex">
+					<img src="${contact.image}">
+					<div class='flex-column'>
+						<span>${contact.first_name} ${contact.last_name}</span>
+						<span>${contact.mobile_no}</span>
+						${customer_link}
+					</div>
+				</div>
+			`);
+		}
+	}
+
+	make_summary_section() {
+		//
+	}
+
+	set_call_status() {
+		let title = '';
+		if (this.call_log.call_status === 'Incoming') {
+			if (this.contact) {
+				title = __('Incoming call from {0}', [this.contact.name]);
+			} else {
+				title = __('Incoming call from unknown number');
+			}
+		} else {
+			title = __('Call Connected');
+		}
+		this.dialog.set_title(title);
+	}
+
+	update(data) {
+		this.call_log = data.call_log;
+		this.set_call_status();
+	}
+}
+
+$(document).on('app_ready', function () {
+	frappe.realtime.on('show_call_popup', data => {
+		if (!erpnext.call_popup) {
+			erpnext.call_popup = new CallPopup(data);
+		} else {
+			erpnext.call_popup.update(data);
+			erpnext.call_popup.dialog.show();
+		}
+	});
+});
diff --git a/erpnext/public/less/call_popup.less b/erpnext/public/less/call_popup.less
new file mode 100644
index 0000000..0ec2066
--- /dev/null
+++ b/erpnext/public/less/call_popup.less
@@ -0,0 +1,7 @@
+.customer-info {
+	img {
+		width: auto;
+		height: 100px;
+		margin-right: 15px;
+	}
+}
\ No newline at end of file