Skip to content

Commit a3845a9

Browse files
authored
feat: Introducing telephony module (frappe#24032)
1 parent ad57eef commit a3845a9

21 files changed

+391
-8
lines changed

.editorconfig

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Root editor config file
2+
root = true
3+
4+
# Common settings
5+
[*]
6+
end_of_line = lf
7+
insert_final_newline = true
8+
trim_trailing_whitespace = true
9+
charset = utf-8
10+
11+
# python, js indentation settings
12+
[{*.py,*.js}]
13+
indent_style = tab
14+
indent_size = 4

erpnext/hooks.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,11 @@
271271
},
272272
"Contact": {
273273
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
274-
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information",
274+
"after_insert": "erpnext.telephony.doctype.call_log.call_log.set_caller_information",
275275
"validate": "erpnext.crm.utils.update_lead_phone_numbers"
276276
},
277277
"Lead": {
278-
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information"
278+
"after_insert": "erpnext.telephony.doctype.call_log.call_log.set_caller_information"
279279
},
280280
"Email Unsubscribe": {
281281
"after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient"

erpnext/modules.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ Hub Node
2525
Quality Management
2626
Communication
2727
Loan Management
28-
Payroll
28+
Payroll
29+
Telephony

erpnext/public/build.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@
4949
"public/js/education/assessment_result_tool.html",
5050
"public/js/hub/hub_factory.js",
5151
"public/js/call_popup/call_popup.js",
52-
"public/js/utils/dimension_tree_filter.js"
52+
"public/js/utils/dimension_tree_filter.js",
53+
"public/js/telephony.js"
5354
],
5455
"js/item-dashboard.min.js": [
5556
"stock/dashboard/item_dashboard.html",

erpnext/public/js/call_popup/call_popup.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class CallPopup {
7474
'click': () => {
7575
const call_summary = this.dialog.get_value('call_summary');
7676
if (!call_summary) return;
77-
frappe.xcall('erpnext.communication.doctype.call_log.call_log.add_call_summary', {
77+
frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary', {
7878
'call_log': this.call_log.name,
7979
'summary': call_summary,
8080
}).then(() => {

erpnext/public/js/telephony.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
frappe.ui.form.ControlData = frappe.ui.form.ControlData.extend( {
2+
make_input() {
3+
this._super();
4+
if (this.df.options == 'Phone') {
5+
this.setup_phone();
6+
}
7+
},
8+
setup_phone() {
9+
if (frappe.phone_call.handler) {
10+
this.$wrapper.find('.control-input')
11+
.append(`
12+
<span class="phone-btn">
13+
<a class="btn-open no-decoration" title="${__('Make a call')}">
14+
<i class="fa fa-phone"></i></a>
15+
</span>
16+
`)
17+
.find('.phone-btn')
18+
.click(() => {
19+
frappe.phone_call.handler(this.get_value(), this.frm);
20+
});
21+
}
22+
}
23+
});

erpnext/telephony/doctype/__init__.py

Whitespace-only changes.

erpnext/telephony/doctype/call_log/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
2+
// For license information, please see license.txt
3+
4+
frappe.ui.form.on('Call Log', {
5+
// refresh: function(frm) {
6+
7+
// }
8+
});

erpnext/communication/doctype/call_log/call_log.json renamed to erpnext/telephony/doctype/call_log/call_log.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,11 @@
137137
"read_only": 1
138138
}
139139
],
140-
"in_create": 1,
141140
"index_web_pages_for_search": 1,
142141
"links": [],
143-
"modified": "2020-08-25 17:08:34.085731",
142+
"modified": "2020-11-25 14:32:44.407815",
144143
"modified_by": "Administrator",
145-
"module": "Communication",
144+
"module": "Telephony",
146145
"name": "Call Log",
147146
"owner": "Administrator",
148147
"permissions": [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
3+
# See license.txt
4+
from __future__ import unicode_literals
5+
6+
# import frappe
7+
import unittest
8+
9+
class TestCallLog(unittest.TestCase):
10+
pass

erpnext/telephony/doctype/incoming_call_handling_schedule/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"actions": [],
3+
"creation": "2020-11-19 11:15:54.967710",
4+
"doctype": "DocType",
5+
"editable_grid": 1,
6+
"engine": "InnoDB",
7+
"field_order": [
8+
"day_of_week",
9+
"from_time",
10+
"to_time",
11+
"agent_group"
12+
],
13+
"fields": [
14+
{
15+
"fieldname": "day_of_week",
16+
"fieldtype": "Select",
17+
"in_list_view": 1,
18+
"label": "Day Of Week",
19+
"options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday",
20+
"reqd": 1
21+
},
22+
{
23+
"default": "9:00:00",
24+
"fieldname": "from_time",
25+
"fieldtype": "Time",
26+
"in_list_view": 1,
27+
"label": "From Time",
28+
"reqd": 1
29+
},
30+
{
31+
"default": "17:00:00",
32+
"fieldname": "to_time",
33+
"fieldtype": "Time",
34+
"in_list_view": 1,
35+
"label": "To Time",
36+
"reqd": 1
37+
},
38+
{
39+
"fieldname": "agent_group",
40+
"fieldtype": "Link",
41+
"in_list_view": 1,
42+
"label": "Agent Group",
43+
"options": "Employee Group",
44+
"reqd": 1
45+
}
46+
],
47+
"index_web_pages_for_search": 1,
48+
"istable": 1,
49+
"links": [],
50+
"modified": "2020-11-19 11:15:54.967710",
51+
"modified_by": "Administrator",
52+
"module": "Telephony",
53+
"name": "Incoming Call Handling Schedule",
54+
"owner": "Administrator",
55+
"permissions": [],
56+
"quick_entry": 1,
57+
"sort_field": "modified",
58+
"sort_order": "DESC",
59+
"track_changes": 1
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
3+
# For license information, please see license.txt
4+
5+
from __future__ import unicode_literals
6+
# import frappe
7+
from frappe.model.document import Document
8+
9+
class IncomingCallHandlingSchedule(Document):
10+
pass

erpnext/telephony/doctype/incoming_call_settings/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
2+
// For license information, please see license.txt
3+
4+
function time_to_seconds(time_str) {
5+
// Convert time string of format HH:MM:SS into seconds.
6+
let seq = time_str.split(':');
7+
seq = seq.map((n) => parseInt(n));
8+
return (seq[0]*60*60) + (seq[1]*60) + seq[2];
9+
}
10+
11+
function number_sort(array, ascending=true) {
12+
let array_copy = [...array];
13+
if (ascending) {
14+
array_copy.sort((a, b) => a-b); // ascending order
15+
} else {
16+
array_copy.sort((a, b) => b-a); // descending order
17+
}
18+
return array_copy;
19+
}
20+
21+
function groupby(items, key) {
22+
// Group the list of items using the given key.
23+
const obj = {};
24+
items.forEach((item) => {
25+
if (item[key] in obj) {
26+
obj[item[key]].push(item);
27+
} else {
28+
obj[item[key]] = [item];
29+
}
30+
});
31+
return obj;
32+
}
33+
34+
function check_timeslot_overlap(ts1, ts2) {
35+
/// Timeslot is a an array of length 2 ex: [from_time, to_time]
36+
/// time in timeslot is an integer represents number of seconds.
37+
if ((ts1[0] < ts2[0] && ts1[1] <= ts2[0]) || (ts1[0] >= ts2[1] && ts1[1] > ts2[1])) {
38+
return false;
39+
}
40+
return true;
41+
}
42+
43+
function validate_call_schedule(schedule) {
44+
validate_call_schedule_timeslot(schedule);
45+
validate_call_schedule_overlaps(schedule);
46+
}
47+
48+
function validate_call_schedule_timeslot(schedule) {
49+
// Make sure that to time slot is ahead of from time slot.
50+
let errors = [];
51+
52+
for (let row in schedule) {
53+
let record = schedule[row];
54+
let from_time_in_secs = time_to_seconds(record.from_time);
55+
let to_time_in_secs = time_to_seconds(record.to_time);
56+
if (from_time_in_secs >= to_time_in_secs) {
57+
errors.push(__('Call Schedule Row {0}: To time slot should always be ahead of From time slot.', [row]));
58+
}
59+
}
60+
61+
if (errors.length > 0) {
62+
frappe.throw(errors.join("<br/>"));
63+
}
64+
}
65+
66+
function is_call_schedule_overlapped(day_schedule) {
67+
// Check if any time slots are overlapped in a day schedule.
68+
let timeslots = [];
69+
day_schedule.forEach((record)=> {
70+
timeslots.push([time_to_seconds(record.from_time), time_to_seconds(record.to_time)]);
71+
});
72+
73+
if (timeslots.length < 2) {
74+
return false;
75+
}
76+
77+
timeslots = number_sort(timeslots);
78+
79+
// Sorted timeslots will be in ascending order if not overlapped.
80+
for (let i=1; i < timeslots.length; i++) {
81+
if (check_timeslot_overlap(timeslots[i-1], timeslots[i])) {
82+
return true;
83+
}
84+
}
85+
return false;
86+
}
87+
88+
function validate_call_schedule_overlaps(schedule) {
89+
let group_by_day = groupby(schedule, 'day_of_week');
90+
for (const [day, day_schedule] of Object.entries(group_by_day)) {
91+
if (is_call_schedule_overlapped(day_schedule)) {
92+
frappe.throw(__('Please fix overlapping time slots for {0}', [day]));
93+
}
94+
}
95+
}
96+
97+
frappe.ui.form.on('Incoming Call Settings', {
98+
validate(frm) {
99+
validate_call_schedule(frm.doc.call_handling_schedule);
100+
}
101+
});
102+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{
2+
"actions": [],
3+
"autoname": "Prompt",
4+
"creation": "2020-11-19 10:37:20.734245",
5+
"doctype": "DocType",
6+
"editable_grid": 1,
7+
"engine": "InnoDB",
8+
"field_order": [
9+
"call_routing",
10+
"column_break_2",
11+
"greeting_message",
12+
"agent_busy_message",
13+
"agent_unavailable_message",
14+
"section_break_6",
15+
"call_handling_schedule"
16+
],
17+
"fields": [
18+
{
19+
"default": "Sequential",
20+
"fieldname": "call_routing",
21+
"fieldtype": "Select",
22+
"in_list_view": 1,
23+
"label": "Call Routing",
24+
"options": "Sequential\nSimultaneous"
25+
},
26+
{
27+
"fieldname": "greeting_message",
28+
"fieldtype": "Data",
29+
"label": "Greeting Message"
30+
},
31+
{
32+
"fieldname": "agent_busy_message",
33+
"fieldtype": "Data",
34+
"label": "Agent Busy Message"
35+
},
36+
{
37+
"fieldname": "agent_unavailable_message",
38+
"fieldtype": "Data",
39+
"label": "Agent Unavailable Message"
40+
},
41+
{
42+
"fieldname": "column_break_2",
43+
"fieldtype": "Column Break"
44+
},
45+
{
46+
"fieldname": "section_break_6",
47+
"fieldtype": "Section Break"
48+
},
49+
{
50+
"fieldname": "call_handling_schedule",
51+
"fieldtype": "Table",
52+
"label": "Call Handling Schedule",
53+
"options": "Incoming Call Handling Schedule",
54+
"reqd": 1
55+
}
56+
],
57+
"index_web_pages_for_search": 1,
58+
"links": [],
59+
"modified": "2020-11-19 11:17:14.527862",
60+
"modified_by": "Administrator",
61+
"module": "Telephony",
62+
"name": "Incoming Call Settings",
63+
"owner": "Administrator",
64+
"permissions": [
65+
{
66+
"create": 1,
67+
"delete": 1,
68+
"email": 1,
69+
"export": 1,
70+
"print": 1,
71+
"read": 1,
72+
"report": 1,
73+
"role": "System Manager",
74+
"share": 1,
75+
"write": 1
76+
}
77+
],
78+
"quick_entry": 1,
79+
"sort_field": "modified",
80+
"sort_order": "DESC",
81+
"track_changes": 1
82+
}

0 commit comments

Comments
 (0)