diff --git a/submodules/strategy/strategy.js b/submodules/strategy/strategy.js index 744e891..4fd887f 100644 --- a/submodules/strategy/strategy.js +++ b/submodules/strategy/strategy.js @@ -570,20 +570,33 @@ define(function(require){ holidayData = { id: val.id, name: val.name, - month: val.month + fromMonth: val.month }; - if("ordinal" in val) { - holidayType = "advanced"; + if(val.hasOwnProperty('ordinal')) { + holidayType = 'advanced'; holidayData.ordinal = val.ordinal; holidayData.wday = val.wdays[0]; - } else { - holidayData.fromDay = val.days[0]; - if(val.days.length > 1) { - holidayType = "range"; - holidayData.toDay = val.days[val.days.length-1]; - } else { - holidayType = "single"; + } + else { + if(val.hasOwnProperty('viewData')) { + holidayType = 'range'; + holidayData.fromDay = val.viewData.fromDay; + holidayData.fromMonth = val.viewData.fromMonth; + holidayData.toDay = val.viewData.toDay; + holidayData.toMonth = val.viewData.toMonth; + holidayData.set = true; + } + else { + holidayData.fromDay = val.days[0]; + if(val.days.length > 1) { + holidayType = 'range'; + holidayData.toDay = val.days[val.days.length-1]; + holidayData.toMonth = val.month; + } + else { + holidayType = 'single'; + } } } @@ -1394,7 +1407,8 @@ define(function(require){ container.on('click', '.delete-holiday', function(e) { var holidaysElement = $(this).parents('.holidays-element'), - id = holidaysElement.data('id'); + id = holidaysElement.data('id'), + type = holidaysElement.data('type'); if(id) { monster.ui.confirm(self.i18n.active().strategy.confirmMessages.deleteHoliday, function() { @@ -1404,23 +1418,22 @@ define(function(require){ self.strategyRebuildMainCallflowRuleArray(strategyData); self.strategyUpdateCallflow(mainCallflow, function(updatedCallflow) { strategyData.callflows["MainCallflow"] = updatedCallflow; - self.callApi({ - resource: 'temporalRule.delete', - data: { - accountId: self.accountId, - ruleId: id - }, - success: function(data, status) { - delete strategyData.temporalRules.holidays[data.data.name]; - holidaysElement.remove(); - } - }); + var afterDelete = function(data) { + delete strategyData.temporalRules.holidays[data.name]; + holidaysElement.remove(); + }; + + if(type === 'set') { + self.strategyDeleteRuleSetAndRules(id, afterDelete); + } + else { + self.strategyDeleteHoliday(id, afterDelete); + } }); }) } else { holidaysElement.remove(); } - }); container.on('click', '.save-button', function(e) { @@ -1433,76 +1446,52 @@ define(function(require){ if(holidaysEnabled) { $.each(container.find('.holidays-element'), function() { - var $this = $(this), - name = $this.find('.name').val().trim(), - month = $this.find('.month :selected').val(), - fromDay = $this.find('.day.from :selected').val(), - toDay = $this.find('.day.to :selected').val(), - ordinal = $this.find('.ordinal :selected').val(), - wday = $this.find('.wday :selected').val(), - id = $this.data('id'), - holidayRule = { - cycle: "yearly", - interval: 1, - month: parseInt(month), - type: "main_holidays" - }; + var holidayRule = self.strategyBuildHolidayRule($(this), holidayRulesRequests); - if(!name || Object.keys(holidayRulesRequests).indexOf(name) >= 0) { + if(!holidayRule) { invalidData = true; return false; } - holidayRule.name = name; - if(fromDay) { - var firstDay = parseInt(fromDay); - holidayRule.days = [firstDay]; - if(toDay) { - var lastDay = parseInt(toDay); - for(var day = firstDay+1; day <= lastDay; day++) { - holidayRule.days.push(day); - } - } - } else { - holidayRule.ordinal = ordinal - holidayRule.wdays = [wday] - } - - if(id) { - holidayRulesRequests[name] = function(callback) { - self.callApi({ - resource: 'temporalRule.update', - data: { - accountId: self.accountId, - ruleId: id, - data: holidayRule - }, - success: function(data, status) { - callback(null, data.data); - } + holidayRulesRequests[holidayRule.name] = function(callback) { + // ghetto strategyBuildHoliday builds a complete different object for a range, so we check if one of the different key is in there, if yes, this is a range spanning multiple months + if(holidayRule.hasOwnProperty('isRange')) { + self.strategyBuildMultiMonthRangeHoliday(holidayRule, function(data) { + data.viewData = holidayRule; + callback && callback(null, data); }); } - } else { - holidayRulesRequests[name] = function(callback) { - self.callApi({ - resource: 'temporalRule.create', - data: { - accountId: self.accountId, - data: holidayRule - }, - success: function(data, status) { - callback(null, data.data); - } + else { + self.strategyCleanUpdateHoliday(holidayRule, function(data) { + callback && callback(null, data); }); } - } - + }; }); if(invalidData) { monster.ui.alert(self.i18n.active().strategy.alertMessages.uniqueHoliday) } else { monster.parallel(holidayRulesRequests, function(err, results) { + // First extract all ids from the new holidayList + var existingHolidaysCallflowsIds = [], + newHolidayCallflowsIds = _.pluck(holidayRulesRequests, 'id'); + + // Find all IDs of existing Callflows in the Main Callflow that are linking to the Main Holidays + _.each(mainCallflow.flow.children, function(directChild, id) { + if(id !== '_' && directChild.data.id === strategyData.callflows["MainHolidays"].id) { + existingHolidaysCallflowsIds.push(id); + } + }); + + // Now see if any of these existing IDs that are no longer in the list of holidays + // If we find orphans, remove them from the main callflow + _.each(existingHolidaysCallflowsIds, function(id) { + if(newHolidayCallflowsIds.indexOf(id) < 0) { + delete mainCallflow.flow.children[id]; + } + }); + _.each(results, function(val, key) { mainCallflow.flow.children[val.id] = { children: {}, @@ -1524,21 +1513,21 @@ define(function(require){ }); } } else { - monster.ui.confirm(self.i18n.active().strategy.confirmMessages.disableHolidays, function() { _.each(strategyData.temporalRules.holidays, function(val, key) { holidayRulesRequests[key] = function(callback) { - self.callApi({ - resource: 'temporalRule.delete', - data: { - accountId: self.accountId, - ruleId: val.id - }, - success: function(data, status) { + if(val.hasOwnProperty('temporal_rules')) { + self.strategyDeleteRuleSetAndRules(val.id, function() { delete mainCallflow.flow.children[val.id]; - callback(null, data.data); - } - }); + callback(null, {}); + }); + } + else { + self.strategyDeleteHoliday(val.id, function() { + delete mainCallflow.flow.children[val.id]; + callback(null, {}); + }); + } } }); @@ -1557,6 +1546,373 @@ define(function(require){ }); }, + strategyCleanUpdateHoliday: function(data, callback) { + var self = this, + updateHoliday = function() { + delete data.extra; + self.strategyUpdateHoliday(data, function(data) { + callback && callback(data); + }); + }; + + if(data.extra.oldType === 'set') { + self.strategyDeleteRuleSetAndRules(data.id, function() { + delete data.id; + updateHoliday(); + }); + } + else { + updateHoliday(); + } + }, + + strategyBuildMultiMonthRangeHoliday: function(data, globalCallback) { + var self = this, + fromDay = parseInt(data.fromDay), + fromMonth = parseInt(data.fromMonth), + toDay = parseInt(data.toDay), + toMonth = parseInt(data.toMonth), + name = data.name, + getMonthRule = function(name, pMonth, pStartDay, pEndDay) { + var month = parseInt(pMonth), + fromDay = pStartDay || 1, + toDay = pEndDay || 31, + days = []; + + for(var day = fromDay; day <= toDay; day++) { + days.push(day); + } + + return { + name: name + '_' + month, + cycle: 'yearly', + days: days, + interval: 1, + month: month + }; + }, + rulesToCreate = [ ], + ruleSet = { + name: name, + temporal_rules: [], + type: 'main_holidays' + }, + parallelRequests = {}, + junkName = name + '_' + monster.util.randomString(6); + + if(fromMonth !== toMonth) { + rulesToCreate.push(getMonthRule(junkName, fromMonth, fromDay, 31)); + + var firstMonthLoop = fromMonth === 12 ? 1 : fromMonth + 1; + + for(var loopMonth = firstMonthLoop; (loopMonth !== toMonth && (loopMonth - 12) !== toMonth); loopMonth++) { + if(loopMonth === 13) { loopMonth = 1 }; + rulesToCreate.push(getMonthRule(junkName, loopMonth, 1, 31)); + } + + rulesToCreate.push(getMonthRule(junkName, toMonth, 1, toDay)); + } + else { + rulesToCreate.push(getMonthRule(junkName, fromMonth, fromDay, toDay)); + } + + _.each(rulesToCreate, function(rule) { + parallelRequests[rule.name] = function(callback) { + self.strategyUpdateHoliday(rule, function(data) { + callback && callback(null, data); + }); + }; + }); + + var createCleanSet = function() { + // Create All Rules, and then Create Rule Set. + monster.parallel(parallelRequests, function(err, results) { + _.each(rulesToCreate, function(rule) { + ruleSet.temporal_rules.push(results[rule.name].id); + }); + + self.strategyCreateRuleSet(ruleSet, function(data) { + globalCallback(data); + }); + }); + }; + + if(data.hasOwnProperty('id')) { + if(data.extra.oldType === 'rule') { + self.strategyDeleteHoliday(data.id, function() { + createCleanSet(); + }); + } + else { + self.strategyDeleteRuleSetAndRules(data.id, function() { + createCleanSet(); + }); + } + } + else { + createCleanSet(); + } + }, + + strategyGetDetailRuleSet: function(id, globalCallback) { + var self = this; + + self.strategyGetRuleSet(id, function(set) { + var parallelRequests = {}; + + _.each(set.temporal_rules, function(ruleId) { + parallelRequests[ruleId] = function(callback) { + self.strategyGetRule(ruleId, function(data) { + if(data.hasOwnProperty('message') && data.message === 'bad identifier') { + data = {}; + } + callback && callback(null, data); + }); + } + }); + + monster.parallel(parallelRequests, function(err, results) { + var ruleDetails = [], + listRules = [], + viewData = {}; + + _.each(set.temporal_rules, function(ruleId) { + if(!_.isEmpty(results[ruleId])) { + listRules.push(ruleId); + ruleDetails.push(results[ruleId]); + } + }); + + if(ruleDetails.length) { + viewData.fromDay = ruleDetails[0].days[0]; + viewData.toDay = ruleDetails[ruleDetails.length-1].days[ruleDetails[ruleDetails.length-1].days.length-1]; + viewData.fromMonth = ruleDetails[0].month; + viewData.toMonth = ruleDetails[ruleDetails.length-1].month; + } + + // If list of actual existing rules isn't the same as the ones in the set, we'll update the set and remove the reference to non-existing rules. + if(!_.isEqual(listRules, set.temporal_rules)) { + // If there is at least one valid rule in the set + if(listRules.length > 0) { + set.temporal_rules = listRules; + // We just want to update the list of rules + self.strategyUpdateRuleSet(set, function(data) { + data.viewData = viewData; + + globalCallback && globalCallback(data); + }); + } + // Otherwise we delete the set + else { + self.strategyDeleteRuleSet(set.id, function() { + globalCallback && globalCallback({}); + }); + } + } + else { + set.viewData = viewData; + + globalCallback && globalCallback(set); + } + }); + }); + + }, + + strategyGetRuleSet: function(id, callback) { + var self = this; + + self.callApi({ + resource: 'temporalSet.get', + data: { + accountId: self.accountId, + setId: id + }, + success: function(data, status) { + callback(data.data); + } + }); + }, + + strategyUpdateRuleSet: function(data, callback) { + var self = this; + + self.callApi({ + resource: 'temporalSet.update', + data: { + accountId: self.accountId, + setId: data.id, + data: data + }, + success: function(data, status) { + callback(data.data); + } + }); + }, + + strategyCreateRuleSet: function(data, callback) { + var self = this; + + self.callApi({ + resource: 'temporalSet.create', + data: { + accountId: self.accountId, + data: data + }, + success: function(data, status) { + callback(data.data); + } + }); + }, + + strategyDeleteRuleSetAndRules: function(id, globalCallback) { + var self = this; + + self.strategyGetRuleSet(id, function(data) { + var parallelRequests = {}; + + _.each(data.temporal_rules, function(id) { + parallelRequests[id] = function(callback) { + self.strategyDeleteHoliday(id, function() { + callback && callback(null, {}); + }); + }; + }); + + monster.parallel(parallelRequests, function(err, results) { + self.strategyDeleteRuleSet(id, function(data) { + globalCallback && globalCallback(data); + }); + }); + }); + }, + + strategyDeleteRuleSet: function(id, callback) { + var self = this; + + self.callApi({ + resource: 'temporalSet.delete', + data: { + accountId: self.accountId, + setId: id + }, + success: function(data, status) { + callback && callback(data.data); + } + }); + }, + + strategyBuildHolidayRule: function(container, rules) { + var self = this, + $this = $(container), + name = $this.find('.name').val().trim(), + month = parseInt($this.find('.month.from :selected').val()), + toMonth = parseInt($this.find('.month.to :selected').val()), + fromDay = parseInt($this.find('.day.from :selected').val()), + toDay = parseInt($this.find('.day.to :selected').val()), + ordinal = $this.find('.ordinal :selected').val(), + wday = $this.find('.wday :selected').val(), + id = $this.data('id'), + type = $this.data('type'), + holidayRule = {}; + + if(!name || Object.keys(rules).indexOf(name) >= 0) { + holidayRule = false; + } + else if(toMonth && month !== toMonth) { + holidayRule = { + isRange: true, + name: name, + fromDay: fromDay, + fromMonth: month, + toDay: toDay, + toMonth: toMonth + }; + } + else { + holidayRule = { + name: name, + cycle: 'yearly', + interval: 1, + month: month, + type: 'main_holidays' + }; + + if(fromDay) { + var firstDay = fromDay; + holidayRule.days = [firstDay]; + if(toDay) { + for(var day = firstDay+1; day <= toDay; day++) { + holidayRule.days.push(day); + } + } + } + else { + holidayRule.ordinal = ordinal + holidayRule.wdays = [wday] + } + } + + if(id) { + holidayRule.id = id; + } + + holidayRule.extra = { + oldType: type + }; + + return holidayRule; + }, + + strategyUpdateHoliday: function(data, callback) { + var self = this; + + if(data.id) { + self.callApi({ + resource: 'temporalRule.update', + data: { + accountId: self.accountId, + ruleId: data.id, + data: data + }, + success: function(data, status) { + callback(data.data); + } + }); + } else { + self.callApi({ + resource: 'temporalRule.create', + data: { + accountId: self.accountId, + data: data + }, + success: function(data, status) { + callback(data.data); + } + }); + } + }, + + strategyDeleteHoliday: function(id, callback) { + var self = this; + + self.callApi({ + resource: 'temporalRule.delete', + data: { + accountId: self.accountId, + ruleId: id, + generateError: false + }, + success: function(data, status) { + callback(data.data); + }, + // Sometimes we'll try to delete a time of day which no longer exist, but still need to execute the callback + error: function(data, status) { + callback(data.data); + } + }); + }, + strategyCallsBindEvents: function(container, strategyData) { var self = this; @@ -2550,59 +2906,93 @@ define(function(require){ }, filters); }, - strategyGetTemporalRules: function(callback) { + strategyGetAllRules: function(globalCallback) { var self = this; + + monster.parallel({ + 'rules': function(localCallback) { + self.callApi({ + resource: 'temporalRule.list', + data: { + accountId: self.accountId, + filters: { 'has_key':'type' } + }, + success: function(data, status) { + localCallback && localCallback(null, data.data); + } + }); + }, + 'sets': function(localCallback) { + self.callApi({ + resource: 'temporalSet.list', + data: { + accountId: self.accountId, + filters: { + has_key:'type', + filter_type: 'main_holidays' + } + }, + success: function(data, status) { + var parallelRequests = {}; + + _.each(data.data, function(set) { + parallelRequests[set.id] = function(callback) { + self.strategyGetDetailRuleSet(set.id, function(data) { + callback && callback(null, data); + }); + }; + }); + + monster.parallel(parallelRequests, function(err, results) { + localCallback && localCallback(null, results); + }); + } + }); + } + }, + function(err, results) { + globalCallback && globalCallback(results); + } + ); + }, + + strategyGetRule: function(id, callback) { + var self = this; + self.callApi({ - resource: 'temporalRule.list', + resource: 'temporalRule.get', data: { accountId: self.accountId, - filters: { 'has_key':'type' } + ruleId: id, + generateError: false }, success: function(data, status) { - var parallelRequests = {}; + callback && callback(data.data); + }, + error: function(data, status) { + callback && callback(data.data); + } + }); + }, - _.each(data.data, function(val, key) { - parallelRequests[val.name] = function(callback) { - self.callApi({ - resource: 'temporalRule.get', - data: { - accountId: self.accountId, - ruleId: val.id - }, - success: function(data, status) { - callback(null, data.data); - } - }); - } - }); + strategyGetTemporalRules: function(callback) { + var self = this; - _.each(self.weekdayLabels, function(val) { - if(!(val in parallelRequests)) { - parallelRequests[val] = function(callback) { - self.callApi({ - resource: 'temporalRule.create', - data: { - accountId: self.accountId, - data: { - cycle: "weekly", - interval: 1, - name: val, - type: "main_weekdays", - time_window_start: 32400, // 9:00AM - time_window_stop: 61200, // 5:00PM - wdays: [val.substring(4).toLowerCase()] - } - }, - success: function(data, status) { - callback(null, data.data); - } - }); - } - } - }); + self.strategyGetAllRules(function(data) { + var parallelRequests = {}; - if(!("MainLunchHours" in parallelRequests)) { - parallelRequests["MainLunchHours"] = function(callback) { + _.each(data.rules, function(val, key) { + parallelRequests[val.name] = function(callback) { + self.strategyGetRule(val.id, function(data) { + callback(null, data); + }); + } + }); + + // Always check that the necessary time rules exist, or re-create them + _.each(self.weekdayLabels, function(val) { + if(!(val in parallelRequests)) { + parallelRequests[val] = function(callback) { self.callApi({ resource: 'temporalRule.create', data: { @@ -2610,11 +3000,11 @@ define(function(require){ data: { cycle: "weekly", interval: 1, - name: "MainLunchHours", - type: "main_lunchbreak", - time_window_start: 43200, - time_window_stop: 46800, - wdays: self.weekdays + name: val, + type: "main_weekdays", + time_window_start: 32400, // 9:00AM + time_window_stop: 61200, // 5:00PM + wdays: [val.substring(4).toLowerCase()] } }, success: function(data, status) { @@ -2623,31 +3013,60 @@ define(function(require){ }); } } + }); - monster.parallel(parallelRequests, function(err, results) { - var temporalRules = { - weekdays: {}, - lunchbreak: {}, - holidays: {} - }; - - _.each(results, function(val, key) { - switch(val.type) { - case "main_weekdays": - temporalRules.weekdays[key] = val - break; - case "main_lunchbreak": - temporalRules.lunchbreak = val; - break; - case "main_holidays": - temporalRules.holidays[key] = val; - break; + if(!("MainLunchHours" in parallelRequests)) { + parallelRequests["MainLunchHours"] = function(callback) { + self.callApi({ + resource: 'temporalRule.create', + data: { + accountId: self.accountId, + data: { + cycle: "weekly", + interval: 1, + name: "MainLunchHours", + type: "main_lunchbreak", + time_window_start: 43200, + time_window_stop: 46800, + wdays: self.weekdays + } + }, + success: function(data, status) { + callback(null, data.data); } }); + } + } + + monster.parallel(parallelRequests, function(err, results) { + var temporalRules = { + weekdays: {}, + lunchbreak: {}, + holidays: {} + }; - callback(temporalRules); + _.each(results, function(val, key) { + switch(val.type) { + case "main_weekdays": + temporalRules.weekdays[key] = val + break; + case "main_lunchbreak": + temporalRules.lunchbreak = val; + break; + case "main_holidays": + temporalRules.holidays[key] = val; + break; + } }); - } + + _.each(data.sets, function(set) { + if(!_.isEmpty(set)) { + temporalRules.holidays[set.name] = set; + } + }); + + callback(temporalRules); + }); }); }, diff --git a/views/strategy-holidayLine.html b/views/strategy-holidayLine.html index 2736040..2639976 100644 --- a/views/strategy-holidayLine.html +++ b/views/strategy-holidayLine.html @@ -1,12 +1,12 @@ -