From 8e8eb8c289ea8efb91fed0d40cd4b0da2f3956d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Guti=C3=A9rrez?= Date: Wed, 31 Oct 2018 10:01:26 -0600 Subject: [PATCH] UI-1161: Reset incoming call strategy (#94) * Add reset button for incoming call handling strategy * Delete menus and callflows for call strategy * Fix code mistakes * Re-create default call strategy after delete * Add missing self variable on delete functions * Small fixes * Refactor code at strategyGetMainCallflows * Refactor to use existing callflow functions * Extract create menu API call to separate function --- i18n/en-US.json | 12 +- submodules/strategy/strategy.js | 589 +++++++++++++----- submodules/strategy/views/strategy-calls.html | 3 +- 3 files changed, 450 insertions(+), 154 deletions(-) diff --git a/i18n/en-US.json b/i18n/en-US.json index 40abaef4..325524a9 100644 --- a/i18n/en-US.json +++ b/i18n/en-US.json @@ -940,7 +940,8 @@ }, "confirmMessages": { "deleteHoliday": "This holiday will be permanently deleted. Continue?", - "disableHolidays": "All existing holidays will be permanently deleted. Continue?" + "disableHolidays": "All existing holidays will be permanently deleted. Continue?", + "resetCalls": "All incoming call handling strategies will be reset. Continue?" }, "alertMessages": { "uniqueHoliday": "Every holiday must have a unique name.", @@ -952,8 +953,9 @@ "lastE911Error": "You need e911 set up on at least one of your Main Numbers. Please set up e911 on another Main Number before deleting this one." }, "toastrMessages": { - "updateHolidaySuccess": "Holidays sucessfully updated.", - "updateCallSuccess": "Call strategy sucessfully updated.", + "updateHolidaySuccess": "Holidays successfully updated.", + "updateCallSuccess": "Call strategy successfully updated.", + "resetCallSuccess": "Call strategy successfully reset.", "buyNumbersSuccess": "The numbers have been purchased.", "buyNumbersError": "An error occured while purchasing numbers.", "removeNumberSuccess": "Number successfully removed from the main callflow." @@ -1034,6 +1036,10 @@ "headerNoE911": "You've disabled E911 on the number that was used for your Emergency Caller-ID, please select a new number to use as this Account Emergency Caller-ID.", "success": "You successfully updated the E911 on the account to be {{ number }}.", "current": "Current one" + }, + + "buttons": { + "reset": "Reset" } }, diff --git a/submodules/strategy/strategy.js b/submodules/strategy/strategy.js index 1f8405c2..1c29c11f 100644 --- a/submodules/strategy/strategy.js +++ b/submodules/strategy/strategy.js @@ -2378,6 +2378,42 @@ define(function(require) { }); } }); + + container.on('click', '.reset-button', function(e) { + e.preventDefault(); + + monster.ui.confirm(self.i18n.active().strategy.confirmMessages.resetCalls, function() { + monster.waterfall([ + function(callback) { + self.strategyDeleteCalls({ + success: function() { + callback(null); + }, + error: function() { + callback(true); + } + }); + }, + function(callback) { + self.strategyGetMainCallflows(function(callflows) { + strategyData.callflows = callflows; + callback(null, callflows); + }); + } + ], function(err, result) { + if (err) { + return; + } + + container.hide(); + container.parents('.element-container').removeClass('open'); + monster.ui.toast({ + type: 'success', + message: self.i18n.active().strategy.toastrMessages.resetCallSuccess + }); + }); + }); + }); }, strategyRenderHolidayLine: function(container, holidayType, holiday) { @@ -2939,50 +2975,55 @@ define(function(require) { }); }, - strategyGetMainCallflows: function(callback) { + strategyGetMainCallflows: function(mainCallback) { var self = this; - self.callApi({ - resource: 'callflow.list', - data: { - accountId: self.accountId, - filters: { - 'has_value': 'type', - 'key_missing': ['owner_id', 'group_id'] - } + monster.waterfall([ + function(waterfallCallback) { + self.strategyListCallflows({ + filters: { + 'has_value': 'type', + 'key_missing': ['owner_id', 'group_id'] + }, + success: function(data) { + waterfallCallback(null, data); + }, + error: function() { + waterfallCallback(true); + } + }); }, - success: function(data, status) { + function(data, waterfallCallback) { var parallelRequests = {}, menuRequests = {}; - _.each(data.data, function(val, key) { - if (val.type === 'main' || val.type === 'conference' || val.type === 'faxing') { - var name = val.name || val.numbers[0]; - if (val.type === 'conference') { - name = 'MainConference'; - } else if (val.type === 'faxing') { - name = 'MainFaxing'; - } - parallelRequests[name] = function(callback) { - self.callApi({ - resource: 'callflow.get', - data: { - accountId: self.accountId, - callflowId: val.id - }, - success: function(data, status) { - callback(null, data.data); - } - }); - }; + _.each(data, function(val, key) { + if (!_.includes(['main', 'conference', 'faxing'], val.type)) { + return; } + + var name = val.name || val.numbers[0]; + if (val.type === 'conference') { + name = 'MainConference'; + } else if (val.type === 'faxing') { + name = 'MainFaxing'; + } + + parallelRequests[name] = function(callback) { + self.strategyGetCallflow({ + data: { + id: val.id + }, + success: function(data) { + callback(null, data); + } + }); + }; }); if (!parallelRequests.MainConference) { parallelRequests.MainConference = function(callback) { - self.callApi({ - resource: 'callflow.create', + self.strategyCreateCallflow({ data: { - accountId: self.accountId, data: { contact_list: { exclude: false @@ -2997,8 +3038,8 @@ define(function(require) { } } }, - success: function(data, status) { - callback(null, data.data); + success: function(data) { + callback(null, data); } }); }; @@ -3006,10 +3047,8 @@ define(function(require) { if (!parallelRequests.MainFaxing) { parallelRequests.MainFaxing = function(callback) { - self.callApi({ - resource: 'callflow.create', + self.strategyCreateCallflow({ data: { - accountId: self.accountId, data: { contact_list: { exclude: false @@ -3024,8 +3063,8 @@ define(function(require) { } } }, - success: function(data, status) { - callback(null, data.data); + success: function(data) { + callback(null, data); } }); }; @@ -3034,139 +3073,159 @@ define(function(require) { _.each(self.subCallflowsLabel, function(val) { var menuName = val + 'Menu'; - if (!parallelRequests[menuName]) { - menuRequests[menuName] = function(callback) { - self.callApi({ - resource: 'menu.create', - data: { - accountId: self.accountId, - data: { - name: menuName, - record_pin: monster.util.randomString(4, '1234567890'), - media: { - exit_media: true, - invalid_media: true, - transfer_media: true - }, - retries: 3, - max_extension_length: 4, - type: 'main' - } - }, - success: function(menuData, status) { - self.callApi({ - resource: 'callflow.create', - data: { - accountId: self.accountId, - data: { - contact_list: { - exclude: false - }, - numbers: [menuName], - type: 'main', - flow: { - children: {}, - data: { - id: menuData.data.id - }, - module: 'menu' - } - } - }, - success: function(data, status) { - callback && callback(null, data.data); - } - }); - } - }); - }; - } else if (!parallelRequests[val]) { + if (parallelRequests[menuName]) { + if (parallelRequests[val]) { + return; + } + menuRequests[menuName] = parallelRequests[menuName]; delete parallelRequests[menuName]; + return; } - }); - monster.parallel(menuRequests, function(err, results) { - var mainCallflows = results; - _.each(self.subCallflowsLabel, function(val) { - if (!parallelRequests[val]) { - parallelRequests[val] = function(callback) { - self.callApi({ - resource: 'callflow.create', + menuRequests[menuName] = function(callback) { + monster.waterfall([ + function(innerCallback) { + self.strategyCreateMenu({ + data: { + data: { + name: menuName, + record_pin: monster.util.randomString(4, '1234567890'), + media: { + exit_media: true, + invalid_media: true, + transfer_media: true + }, + retries: 3, + max_extension_length: 4, + type: 'main' + } + }, + success: function(menuData) { + innerCallback(null, menuData); + }, + error: function(parsedError) { + innerCallback(true); + } + }); + }, + function(menuData, innerCallback) { + self.strategyCreateCallflow({ data: { - accountId: self.accountId, data: { contact_list: { exclude: false }, - numbers: [val], + numbers: [menuName], type: 'main', flow: { children: {}, data: { - id: mainCallflows[val + 'Menu'].id + id: menuData.id }, - module: 'callflow' + module: 'menu' } } }, - success: function(data, status) { - callback(null, data.data); + success: function(data) { + innerCallback(null, data); + }, + error: function(parsedError) { + innerCallback(true); } }); - }; + } + ], function(err, result) { + !err && callback && callback(null, result); + }); + }; + }); + + monster.parallel(menuRequests, function(err, mainCallflows) { + _.each(self.subCallflowsLabel, function(val) { + if (parallelRequests[val]) { + return; } - }); - monster.parallel(parallelRequests, function(err, results) { - if (!parallelRequests.MainCallflow) { - self.callApi({ - resource: 'callflow.create', + parallelRequests[val] = function(callback) { + self.strategyCreateCallflow({ data: { - accountId: self.accountId, data: { contact_list: { exclude: false }, - numbers: ['undefinedMainNumber'], - name: 'MainCallflow', + numbers: [val], type: 'main', flow: { - children: { - '_': { - children: {}, - data: { - id: results.MainOpenHours.id - }, - module: 'callflow' - } + children: {}, + data: { + id: mainCallflows[val + 'Menu'].id }, - data: {}, - module: 'temporal_route' + module: 'callflow' } } }, - success: function(data, status) { - results.MainCallflow = data.data; - callback($.extend(true, mainCallflows, results)); + success: function(data) { + callback(null, data); } }); - } else { + }; + }); + + monster.parallel(parallelRequests, function(err, results) { + if (parallelRequests.MainCallflow) { // For users who had undesired callflow with only "0" in it, we migrate it to our new empty main callflow "undefinedMainNumber" if (results.MainCallflow.numbers && results.MainCallflow.numbers.length === 1 && results.MainCallflow.numbers[0] === '0') { results.MainCallflow.numbers[0] = 'undefinedMainNumber'; self.strategyUpdateCallflow(results.MainCallflow, function(updatedCallflow) { results.MainCallflow = updatedCallflow; - callback($.extend(true, mainCallflows, results)); + waterfallCallback(null, $.extend(true, mainCallflows, results)); }); - } else { - callback($.extend(true, mainCallflows, results)); + return; } + + waterfallCallback(null, $.extend(true, mainCallflows, results)); + return; } + + self.strategyCreateCallflow({ + data: { + data: { + contact_list: { + exclude: false + }, + numbers: ['undefinedMainNumber'], + name: 'MainCallflow', + type: 'main', + flow: { + children: { + '_': { + children: {}, + data: { + id: results.MainOpenHours.id + }, + module: 'callflow' + } + }, + data: {}, + module: 'temporal_route' + } + } + }, + success: function(data) { + results.MainCallflow = data; + waterfallCallback(null, $.extend(true, mainCallflows, results)); + } + }); }); }); } + ], function(err, result) { + if (err) { + return; + } + mainCallback(result); }); }, @@ -3205,8 +3264,13 @@ define(function(require) { } listRequests.push(function(localCallback) { - self.strategyCreateCallflow(callflow, function(data) { - localCallback && localCallback(null, data); + self.strategyCreateCallflow({ + data: { + data: callflow + }, + success: function(data) { + localCallback && localCallback(null, data); + } }); }); } @@ -3223,15 +3287,17 @@ define(function(require) { }, strategyGetFeatureCodes: function(callback) { - var self = this, - filters = { + var self = this; + + self.strategyListCallflows({ + filters: { paginate: 'false', has_key: 'featurecode' - }; - - self.strategyGetCallflows(function(listFeatureCodes) { - callback && callback(listFeatureCodes); - }, filters); + }, + success: function(listFeatureCodes) { + callback && callback(listFeatureCodes); + } + }); }, strategyGetAllRules: function(globalCallback) { @@ -3404,10 +3470,13 @@ define(function(require) { monster.parallel( { callQueues: function(_callback) { - self.strategyGetCallflows(function(callQueuesData) { - _callback(null, callQueuesData); - }, { - 'filter_flow.module': 'qubicle' + self.strategyListCallflows({ + filters: { + 'filter_flow.module': 'qubicle' + }, + success: function(callQueuesData) { + _callback(null, callQueuesData); + } }); }, users: function(_callback) { @@ -3505,10 +3574,13 @@ define(function(require) { }); }, advancedCallflows: function(_callback) { - self.strategyGetCallflows(function(advancedCallflowsData) { - _callback(null, advancedCallflowsData); - }, { - 'filter_ui_is_main_number_cf': true + self.strategyListCallflows({ + filters: { + 'filter_ui_is_main_number_cf': true + }, + success: function(advancedCallflowsData) { + _callback(null, advancedCallflowsData); + } }); } }, @@ -3664,32 +3736,35 @@ define(function(require) { mainCallflow.flow.data.rules = ruleArray; }, - strategyGetCallflows: function(callback, filters) { + strategyListCallflows: function(args) { var self = this; self.callApi({ resource: 'callflow.list', data: { accountId: self.accountId, - filters: filters || {} + filters: args.filters || {} }, success: function(callflowData) { - callback && callback(callflowData.data); + args.hasOwnProperty('success') && args.success(callflowData.data); + }, + error: function(parsedError) { + args.hasOwnProperty('error') && args.error(parsedError); } }); }, - strategyCreateCallflow: function(callflow, callback) { + strategyCreateCallflow: function(args) { var self = this; self.callApi({ resource: 'callflow.create', data: { accountId: self.accountId, - data: callflow + data: args.data.data }, success: function(callflowData) { - callback && callback(callflowData.data); + args.hasOwnProperty('success') && args.success(callflowData.data); } }); }, @@ -3864,6 +3939,220 @@ define(function(require) { args.hasOwnProperty('error') && args.error(); } }); + }, + + strategyGetCallflow: function(args) { + var self = this; + + self.callApi({ + resource: 'callflow.get', + data: { + accountId: self.accountId, + callflowId: args.data.id + }, + success: function(data, status) { + args.hasOwnProperty('success') && args.success(data.data); + }, + error: function(parsedError) { + args.hasOwnProperty('error') && args.error(parsedError); + } + }); + }, + + strategyDeleteCalls: function(args) { + var self = this; + + monster.waterfall([ + // Get main callflows created via SmartPBX + function(callback) { + self.strategyListCallflows({ + filters: { + 'paginate': false, + 'has_value': 'type', + 'filter_type': 'main', + 'key_missing': [ + 'owner_id', + 'group_id' + ], + 'filter_ui_metadata.origin': [ + 'voip' + ] + }, + success: function(data) { + // Convert callflows array to map object, then send to next step + callback(null, + _.reduce(data, function(obj, callflow) { + var label = callflow.name || callflow.numbers[0]; + obj[label] = callflow; + return obj; + }, {})); + }, + error: function(parsedError) { + callback(parsedError); + } + }); + }, + // Delete menus and callflows + function(mainCallflows, callback) { + monster.parallel( + _.reduce(self.subCallflowsLabel, function(parallelCalls, label) { + var deleteSequence = self.strategyCreateSingleCallStrategyDeleteSequence(label, mainCallflows); + + if (_.isEmpty(deleteSequence)) { + return parallelCalls; + } + + parallelCalls.push(function(callback) { + monster.waterfall(deleteSequence, function(err, results) { + callback(null); + }); + }); + + return parallelCalls; + }, []), + function(err, results) { + if (err) { + callback(err); + return; + } + callback(null); + }); + } + ], function(err, results) { + if (err) { + args.hasOwnProperty('error') && args.error(err); + return; + } + args.hasOwnProperty('success') && args.success(results); + }); + }, + + strategyCreateSingleCallStrategyDeleteSequence: function(label, mainCallflows) { + var self = this, + deleteSequence = [], + strategyCallflow = mainCallflows[label], + menuCallflow = mainCallflows[label + 'Menu']; + + if (strategyCallflow) { + // Add function to delete call strategy callflow + deleteSequence.push(function(callback) { + self.strategyDeleteCallflow({ + data: { + id: strategyCallflow.id + }, + success: function() { + callback(null); + }, + error: function() { + callback(true); + } + }); + }); + } + + if (!menuCallflow) { + return deleteSequence; + } + + // There is a menu callflow, so create functions to... + + // ...get callflow details (to get menu ID),... + deleteSequence.push(function(callback) { + self.strategyGetCallflow({ + data: { + id: menuCallflow.id + }, + success: function(menuCallflowDetails) { + callback(null, menuCallflowDetails); + }, + error: function() { + callback(true); + } + }); + }); + + // ...then delete menu callflow + deleteSequence.push(function(menuCallflowDetails, callback) { + self.strategyDeleteCallflow({ + data: { + id: menuCallflow.id + }, + success: function() { + callback(null, menuCallflowDetails); + }, + error: function() { + callback(true); + } + }); + }); + + // ...and finally delete menu + deleteSequence.push(function(menuCallflowDetails, callback) { + self.strategyDeleteMenu({ + data: { + id: menuCallflowDetails.flow.data.id + }, + success: function() { + callback(null); + }, + error: function() { + callback(true); + } + }); + }); + + return deleteSequence; + }, + + strategyCreateMenu: function(args) { + var self = this; + self.callApi({ + resource: 'menu.create', + data: { + accountId: self.accountId, + data: args.data.data + }, + success: function(data, status) { + args.hasOwnProperty('success') && args.success(data.data); + }, + error: function(parsedError) { + args.hasOwnProperty('error') && args.error(parsedError); + } + }); + }, + + strategyDeleteMenu: function(args) { + var self = this; + self.callApi({ + resource: 'menu.delete', + data: { + accountId: self.accountId, + menuId: args.data.id + }, + success: function(data, status) { + args.hasOwnProperty('success') && args.success(data.data); + }, + error: function(parsedError) { + args.hasOwnProperty('error') && args.error(parsedError); + } + }); + }, + + strategyDeleteCallflow: function(args) { + var self = this; + self.callApi({ + resource: 'callflow.delete', + data: { + accountId: self.accountId, + callflowId: args.data.id + }, + success: function(data, status) { + args.hasOwnProperty('success') && args.success(data.data); + }, + error: function(parsedError) { + args.hasOwnProperty('error') && args.error(parsedError); + } + }); } }; diff --git a/submodules/strategy/views/strategy-calls.html b/submodules/strategy/views/strategy-calls.html index dc82d30d..a5ff9661 100644 --- a/submodules/strategy/views/strategy-calls.html +++ b/submodules/strategy/views/strategy-calls.html @@ -32,6 +32,7 @@
+ {{i18n.cancel}} -
\ No newline at end of file +