Merge pull request #24581 from surajshetty3416/fix-telephony
This commit is contained in:
commit
fbc3988037
@ -352,7 +352,7 @@ def get_lead_with_phone_number(number):
|
||||
leads = frappe.get_all('Lead', or_filters={
|
||||
'phone': ['like', '%{}'.format(number)],
|
||||
'mobile_no': ['like', '%{}'.format(number)]
|
||||
}, limit=1)
|
||||
}, limit=1, order_by="creation DESC")
|
||||
|
||||
lead = leads[0].name if leads else None
|
||||
|
||||
@ -361,4 +361,4 @@ def get_lead_with_phone_number(number):
|
||||
def daily_open_lead():
|
||||
leads = frappe.get_all("Lead", filters = [["contact_date", "Between", [nowdate(), nowdate()]]])
|
||||
for lead in leads:
|
||||
frappe.db.set_value("Lead", lead.name, "status", "Open")
|
||||
frappe.db.set_value("Lead", lead.name, "status", "Open")
|
||||
|
@ -2,7 +2,7 @@
|
||||
"css/erpnext.css": [
|
||||
"public/less/erpnext.less",
|
||||
"public/less/hub.less",
|
||||
"public/less/call_popup.less",
|
||||
"public/scss/call_popup.scss",
|
||||
"public/scss/point-of-sale.scss"
|
||||
],
|
||||
"css/marketplace.css": [
|
||||
|
@ -7,10 +7,103 @@ class CallPopup {
|
||||
}
|
||||
|
||||
make() {
|
||||
frappe.utils.play_sound('incoming-call');
|
||||
this.dialog = new frappe.ui.Dialog({
|
||||
'static': true,
|
||||
'minimizable': true,
|
||||
'fields': [{
|
||||
'minimizable': true
|
||||
});
|
||||
this.dialog.get_close_btn().show();
|
||||
this.setup_dialog();
|
||||
this.set_call_status();
|
||||
frappe.utils.bind_actions_with_object(this.dialog.$body, this);
|
||||
this.dialog.$wrapper.addClass('call-popup');
|
||||
this.dialog.get_close_btn().unbind('click').click(this.close_modal.bind(this));
|
||||
this.dialog.show();
|
||||
}
|
||||
|
||||
setup_dialog() {
|
||||
this.setup_call_details();
|
||||
this.dialog.$body.empty().append(this.caller_info);
|
||||
}
|
||||
|
||||
set_indicator(color, blink=false) {
|
||||
let classes = `indicator ${color} ${blink ? 'blink': ''}`;
|
||||
this.dialog.header.find('.indicator').attr('class', classes);
|
||||
}
|
||||
|
||||
set_call_status(call_status) {
|
||||
let title = '';
|
||||
call_status = call_status || this.call_log.status;
|
||||
if (['Ringing'].includes(call_status) || !call_status) {
|
||||
title = __('Incoming call from {0}', [this.get_caller_name() || this.caller_number]);
|
||||
this.set_indicator('blue', true);
|
||||
} else if (call_status === 'In Progress') {
|
||||
title = __('Call Connected');
|
||||
this.set_indicator('green');
|
||||
} else if (['No Answer', 'Missed'].includes(call_status)) {
|
||||
this.set_indicator('yellow');
|
||||
title = __('Call Missed');
|
||||
} else if (['Completed', 'Busy', 'Failed'].includes(call_status)) {
|
||||
this.set_indicator('red');
|
||||
title = __('Call Ended');
|
||||
} else {
|
||||
this.set_indicator('blue');
|
||||
title = call_status;
|
||||
}
|
||||
this.dialog.set_title(title);
|
||||
}
|
||||
|
||||
update_call_log(call_log, missed) {
|
||||
this.call_log = call_log;
|
||||
this.set_call_status(missed ? 'Missed': null);
|
||||
}
|
||||
|
||||
close_modal() {
|
||||
this.dialog.hide();
|
||||
delete erpnext.call_popup;
|
||||
}
|
||||
|
||||
call_ended(call_log, missed) {
|
||||
frappe.utils.play_sound('call-disconnect');
|
||||
this.update_call_log(call_log, missed);
|
||||
setTimeout(() => {
|
||||
if (!this.dialog.get_value('call_summary')) {
|
||||
this.close_modal();
|
||||
}
|
||||
}, 60000);
|
||||
this.clear_listeners();
|
||||
}
|
||||
|
||||
get_caller_name() {
|
||||
const contact_link = this.get_contact_link();
|
||||
return contact_link.link_title || contact_link.link_name;
|
||||
}
|
||||
|
||||
get_contact_link() {
|
||||
let log = this.call_log;
|
||||
let contact_link = log.links.find(d => d.link_doctype === 'Contact');
|
||||
return contact_link || {};
|
||||
}
|
||||
|
||||
setup_listener() {
|
||||
frappe.realtime.on(`call_${this.call_log.id}_ended`, call_log => {
|
||||
this.call_ended(call_log);
|
||||
});
|
||||
|
||||
frappe.realtime.on(`call_${this.call_log.id}_missed`, call_log => {
|
||||
this.call_ended(call_log, true);
|
||||
});
|
||||
}
|
||||
|
||||
clear_listeners() {
|
||||
frappe.realtime.off(`call_${this.call_log.id}_ended`);
|
||||
frappe.realtime.off(`call_${this.call_log.id}_missed`);
|
||||
}
|
||||
|
||||
setup_call_details() {
|
||||
this.caller_info = $(`<div></div>`);
|
||||
this.call_details = new frappe.ui.FieldGroup({
|
||||
fields: [{
|
||||
'fieldname': 'name',
|
||||
'label': 'Name',
|
||||
'default': this.get_caller_name() || __('Unknown Caller'),
|
||||
@ -19,17 +112,17 @@ class CallPopup {
|
||||
}, {
|
||||
'fieldtype': 'Button',
|
||||
'label': __('Open Contact'),
|
||||
'click': () => frappe.set_route('Form', 'Contact', this.call_log.contact),
|
||||
'depends_on': () => this.call_log.contact
|
||||
}, {
|
||||
'fieldtype': 'Button',
|
||||
'label': __('Open Lead'),
|
||||
'click': () => frappe.set_route('Form', 'Lead', this.call_log.lead),
|
||||
'depends_on': () => this.call_log.lead
|
||||
'click': () => frappe.set_route('Form', 'Contact', this.get_contact_link().link_name),
|
||||
'depends_on': () => this.get_caller_name()
|
||||
}, {
|
||||
'fieldtype': 'Button',
|
||||
'label': __('Create New Contact'),
|
||||
'click': () => frappe.new_doc('Contact', { 'mobile_no': this.caller_number }),
|
||||
'click': this.create_new_contact.bind(this),
|
||||
'depends_on': () => !this.get_caller_name()
|
||||
}, {
|
||||
'fieldtype': 'Button',
|
||||
'label': __('Create New Customer'),
|
||||
'click': this.create_new_customer.bind(this),
|
||||
'depends_on': () => !this.get_caller_name()
|
||||
}, {
|
||||
'fieldtype': 'Button',
|
||||
@ -44,26 +137,9 @@ class CallPopup {
|
||||
'fieldtype': 'Data',
|
||||
'default': this.caller_number,
|
||||
'read_only': 1
|
||||
}, {
|
||||
'fielname': 'last_interaction',
|
||||
'fieldtype': 'Section Break',
|
||||
'label': __('Activity'),
|
||||
'depends_on': () => this.get_caller_name()
|
||||
}, {
|
||||
'fieldtype': 'Small Text',
|
||||
'label': __('Last Issue'),
|
||||
'fieldname': 'last_issue',
|
||||
'read_only': true,
|
||||
'depends_on': () => this.call_log.contact,
|
||||
'default': `<i class="text-muted">${__('No issue has been raised by the caller.')}<i>`
|
||||
}, {
|
||||
'fieldtype': 'Small Text',
|
||||
'label': __('Last Communication'),
|
||||
'fieldname': 'last_communication',
|
||||
'read_only': true,
|
||||
'default': `<i class="text-muted">${__('No communication found.')}<i>`
|
||||
}, {
|
||||
'fieldtype': 'Section Break',
|
||||
'hide_border': 1,
|
||||
}, {
|
||||
'fieldtype': 'Small Text',
|
||||
'label': __('Call Summary'),
|
||||
@ -72,7 +148,7 @@ class CallPopup {
|
||||
'fieldtype': 'Button',
|
||||
'label': __('Save'),
|
||||
'click': () => {
|
||||
const call_summary = this.dialog.get_value('call_summary');
|
||||
const call_summary = this.call_details.get_value('call_summary');
|
||||
if (!call_summary) return;
|
||||
frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary', {
|
||||
'call_log': this.call_log.name,
|
||||
@ -94,108 +170,42 @@ class CallPopup {
|
||||
});
|
||||
}
|
||||
}],
|
||||
body: this.caller_info
|
||||
});
|
||||
this.set_call_status();
|
||||
this.dialog.get_close_btn().show();
|
||||
this.make_last_interaction_section();
|
||||
this.dialog.$body.addClass('call-popup');
|
||||
this.dialog.set_secondary_action(this.close_modal.bind(this));
|
||||
frappe.utils.play_sound('incoming-call');
|
||||
this.dialog.show();
|
||||
this.call_details.make();
|
||||
}
|
||||
|
||||
set_indicator(color, blink=false) {
|
||||
let classes = `indicator ${color} ${blink ? 'blink': ''}`;
|
||||
this.dialog.header.find('.indicator').attr('class', classes);
|
||||
is_known_caller() {
|
||||
return Boolean(this.get_caller_name());
|
||||
}
|
||||
|
||||
set_call_status(call_status) {
|
||||
let title = '';
|
||||
call_status = call_status || this.call_log.status;
|
||||
if (['Ringing'].includes(call_status) || !call_status) {
|
||||
title = __('Incoming call from {0}', [this.get_caller_name() || this.caller_number]);
|
||||
this.set_indicator('blue', true);
|
||||
} else if (call_status === 'In Progress') {
|
||||
title = __('Call Connected');
|
||||
this.set_indicator('yellow');
|
||||
} else if (call_status === 'Missed') {
|
||||
this.set_indicator('red');
|
||||
title = __('Call Missed');
|
||||
} else if (['Completed', 'Disconnected'].includes(call_status)) {
|
||||
this.set_indicator('red');
|
||||
title = __('Call Disconnected');
|
||||
} else {
|
||||
this.set_indicator('blue');
|
||||
title = call_status;
|
||||
}
|
||||
this.dialog.set_title(title);
|
||||
create_new_customer() {
|
||||
// to avoid quick entry form
|
||||
const new_customer = frappe.model.get_new_doc('Customer');
|
||||
new_customer.mobile_no = this.caller_number;
|
||||
frappe.set_route('Form', new_customer.doctype, new_customer.name);
|
||||
}
|
||||
|
||||
update_call_log(call_log) {
|
||||
this.call_log = call_log;
|
||||
this.set_call_status();
|
||||
}
|
||||
|
||||
close_modal() {
|
||||
this.dialog.hide();
|
||||
delete erpnext.call_popup;
|
||||
}
|
||||
|
||||
call_disconnected(call_log) {
|
||||
frappe.utils.play_sound('call-disconnect');
|
||||
this.update_call_log(call_log);
|
||||
setTimeout(() => {
|
||||
if (!this.dialog.get_value('call_summary')) {
|
||||
this.close_modal();
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
make_last_interaction_section() {
|
||||
frappe.xcall('erpnext.crm.doctype.utils.get_last_interaction', {
|
||||
'contact': this.call_log.contact,
|
||||
'lead': this.call_log.lead
|
||||
}).then(data => {
|
||||
const comm_field = this.dialog.get_field('last_communication');
|
||||
if (data.last_communication) {
|
||||
const comm = data.last_communication;
|
||||
comm_field.set_value(comm.content);
|
||||
}
|
||||
|
||||
if (data.last_issue) {
|
||||
const issue = data.last_issue;
|
||||
const issue_field = this.dialog.get_field("last_issue");
|
||||
issue_field.set_value(issue.subject);
|
||||
issue_field.$wrapper.append(`
|
||||
<a class="text-medium" href="/app/issue?customer=${issue.customer}">
|
||||
${__('View all issues from {0}', [issue.customer])}
|
||||
</a>
|
||||
`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get_caller_name() {
|
||||
let log = this.call_log;
|
||||
return log.contact_name || log.lead_name;
|
||||
}
|
||||
|
||||
setup_listener() {
|
||||
frappe.realtime.on(`call_${this.call_log.id}_disconnected`, call_log => {
|
||||
this.call_disconnected(call_log);
|
||||
// Remove call disconnect listener after the call is disconnected
|
||||
frappe.realtime.off(`call_${this.call_log.id}_disconnected`);
|
||||
});
|
||||
create_new_contact() {
|
||||
// TODO: fix new_doc, it should accept child table values
|
||||
const new_contact = frappe.model.get_new_doc('Contact');
|
||||
const phone_no = frappe.model.add_child(new_contact, 'Contact Phone', 'phone_nos');
|
||||
phone_no.phone = this.caller_number;
|
||||
phone_no.is_primary_mobile_no = 1;
|
||||
frappe.set_route('Form', new_contact.doctype, new_contact.name);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).on('app_ready', function () {
|
||||
frappe.realtime.on('show_call_popup', call_log => {
|
||||
if (!erpnext.call_popup) {
|
||||
let call_popup = erpnext.call_popup;
|
||||
if (call_popup && call_log.name === call_popup.call_log.name) {
|
||||
erpnext.call_popup = new CallPopup(call_log);
|
||||
} else {
|
||||
erpnext.call_popup.update_call_log(call_log);
|
||||
erpnext.call_popup.dialog.show();
|
||||
call_popup.update_call_log(call_log);
|
||||
call_popup.dialog.show();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
window.CallPopup = CallPopup;
|
||||
|
@ -1,32 +1,31 @@
|
||||
<div class="call-detail-wrapper">
|
||||
<div class="left-arrow"></div>
|
||||
<div class="head text-muted">
|
||||
<div class="head flex justify-between">
|
||||
<div>
|
||||
<span class="bold"> {{ type }} Call</span>
|
||||
{% if (duration) %}
|
||||
<span class="text-muted"> • {{ frappe.format(duration, { fieldtype: "Duration" }) }}</span>
|
||||
{% endif %}
|
||||
<span class="text-muted"> • {{ comment_when(creation) }}</span>
|
||||
</div>
|
||||
<span>
|
||||
<i class="fa fa-phone"> </i>
|
||||
<span>
|
||||
<span> {{ type }} Call</span>
|
||||
-
|
||||
<span> {{ frappe.format(duration, { fieldtype: "Duration" }) }}</span>
|
||||
-
|
||||
<span> {{ comment_when(creation) }}</span>
|
||||
-
|
||||
<!-- <span> {{ status }}</span>
|
||||
- -->
|
||||
<a class="text-muted" href="#Form/Call Log/{{name}}">Details</a>
|
||||
{% if (show_call_button) { %}
|
||||
<a class="pull-right">Callback</a>
|
||||
{% } %}
|
||||
<a class="action-btn" href="/app/call-log/{{ name }}" title="{{ __("Open Call Log") }}">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-link-url" class="like-icon"></use>
|
||||
</svg>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="body padding">
|
||||
|
||||
|
||||
<div class="body pt-3">
|
||||
{% if (type === "Incoming") { %}
|
||||
<span> Incoming call from {{ from }}, received by {{ to }}</span>
|
||||
{% } else { %}
|
||||
<span> Outgoing Call made by {{ from }} to {{ to }}</span>
|
||||
{% } %}
|
||||
<hr>
|
||||
<div class="summary">
|
||||
<div class="summary pt-3">
|
||||
{% if (summary) { %}
|
||||
<span>{{ summary }}</span>
|
||||
<i>{{ summary }}</i>
|
||||
{% } else { %}
|
||||
<i class="text-muted">{{ __("No Summary") }}</i>
|
||||
{% } %}
|
||||
|
@ -1,9 +0,0 @@
|
||||
.call-popup {
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.for-description {
|
||||
max-height: 250px;
|
||||
overflow: scroll;
|
||||
}
|
||||
}
|
21
erpnext/public/scss/call_popup.scss
Normal file
21
erpnext/public/scss/call_popup.scss
Normal file
@ -0,0 +1,21 @@
|
||||
.call-popup {
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.for-description {
|
||||
max-height: 250px;
|
||||
overflow: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
audio {
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
background-color: var(--control-bg);
|
||||
border-radius: var(--border-radius-sm);
|
||||
&-webkit-media-controls-panel {
|
||||
background: var(--control-bg);
|
||||
}
|
||||
outline: none;
|
||||
}
|
@ -2,7 +2,26 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Call Log', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
refresh: function(frm) {
|
||||
frm.events.setup_recording_audio_control(frm);
|
||||
const incoming_call = frm.doc.type == 'Incoming';
|
||||
frm.add_custom_button(incoming_call ? __('Callback'): __('Call Again'), () => {
|
||||
const number = incoming_call ? frm.doc.from : frm.doc.to;
|
||||
frappe.phone_call.handler(number, frm);
|
||||
});
|
||||
},
|
||||
setup_recording_audio_control(frm) {
|
||||
const recording_wrapper = frm.get_field('recording_html').$wrapper;
|
||||
if (!frm.doc.recording_url || frm.doc.recording_url == 'null') {
|
||||
recording_wrapper.empty();
|
||||
} else {
|
||||
recording_wrapper.addClass('input-max-width');
|
||||
recording_wrapper.html(`
|
||||
<audio
|
||||
controls
|
||||
src="${frm.doc.recording_url}">
|
||||
</audio>
|
||||
`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -5,6 +5,7 @@
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"call_details_section",
|
||||
"id",
|
||||
"from",
|
||||
"to",
|
||||
@ -21,20 +22,9 @@
|
||||
"section_break_11",
|
||||
"summary",
|
||||
"section_break_19",
|
||||
"links",
|
||||
"column_break_3",
|
||||
"section_break_5"
|
||||
"links"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Call Details"
|
||||
},
|
||||
{
|
||||
"fieldname": "id",
|
||||
"fieldtype": "Data",
|
||||
@ -75,6 +65,7 @@
|
||||
{
|
||||
"fieldname": "recording_url",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Recording URL"
|
||||
},
|
||||
{
|
||||
@ -112,13 +103,13 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "summary",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Call Summary"
|
||||
"fieldtype": "Small Text"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_11",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
"hide_border": 1,
|
||||
"label": "Call Summary"
|
||||
},
|
||||
{
|
||||
"fieldname": "start_time",
|
||||
@ -138,12 +129,17 @@
|
||||
"label": "Customer",
|
||||
"options": "Customer",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "call_details_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Call Details"
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-01-13 12:28:20.288985",
|
||||
"modified": "2021-02-08 14:23:28.744844",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Telephony",
|
||||
"name": "Call Log",
|
||||
|
@ -165,6 +165,8 @@ def get_linked_call_logs(doctype, docname):
|
||||
for log in logs:
|
||||
log.show_call_button = 0
|
||||
timeline_contents.append({
|
||||
'icon': 'call',
|
||||
'is_card': True,
|
||||
'creation': log.creation,
|
||||
'template': 'call_link',
|
||||
'template_data': log
|
||||
|
Loading…
x
Reference in New Issue
Block a user