"use strict"; /** * Created by abhatkha on 9/08/2017. */ (function () { 'use strict'; angular.module('adminModule') .controller('LaunchActionController', ['$filter', '$modal', '$rootScope', '$scope', '$log', '$q', 'events', 'metadataModel', 'screenConfigurationModel', 'screenConfigurationService', 'systemAlertService', 'layoutConfigurationModel', 'objectValueMapperService', '$window', 'expressionEventManager', 'expressionEvaluatorService', '$timeout', function ($filter, $modal, $rootScope, $scope, $log, $q, events, metadataModel, screenConfigurationModel, screenConfigurationService, systemAlertService, layoutConfigurationModel, objectValueMapperService, $window, expressionEventManager, expressionEvaluatorService, $timeout) { var promptModal, metadata, contextId, type, isExecutingProviderAction; $scope.providerActionToExecute = objectValueMapperService.providerActionToExecute; $scope.context = $scope.context ? $scope.context : $scope.ticket; if ($scope.bulkContextType) { type = $scope.bulkContextType; } else { type = $scope.context.ticketType || $scope.context.type; } metadataModel.getMetadataByType(type).then(function (data) { metadata = data; }); var fieldPropertyMapping = function (actionLists) { $scope.actionLists = actionLists; $scope.fieldActionMapping = {}; for (var i in actionLists) { if (actionLists[i].mappedFields && actionLists[i].mappedFields.length > 0) { var fieldArr = actionLists[i].mappedFields, fieldObjArr = _.uniqBy(fieldArr, function (field) { return field.fieldName; }); for (var j in fieldObjArr) { $scope.fieldActionMapping[fieldObjArr[j].fieldName] = { action: actionLists[i], iconName: fieldObjArr[j].iconName }; } } } //store the fields with actions map in objectValueMapperService objectValueMapperService.setFieldActionMapping($scope.fieldActionMapping, $scope.context.type); }; var extractProviderActionExpressions = function (actionLists) { var isActionOnTicketLoad; if (actionLists) { _.forEach(actionLists, function (action) { if (action.expressions && action.expressions.length) { _.forEach(action.expressions, function (expression) { objectValueMapperService.addToProviderActionExpressionMapper(expression.condition, action); if (expression.executeOn === 'On Ticket Load') { isActionOnTicketLoad = true; } }); } }); if (isActionOnTicketLoad) { expressionEventManager.handleTicketLoadForActions(); } } }; if (EntityVO.ENTITIES_WITH_NEW_CUSTOMIZATION.indexOf(type) !== -1) { screenConfigurationModel.providerHardReload = true; $scope.associateActionPromise = screenConfigurationModel.loadActionsNew($scope.context.type).then(function () { $scope.AssociateActionLists = screenConfigurationModel.actionList; fieldPropertyMapping($scope.AssociateActionLists); if (screenConfigurationModel.isTicketEnabledForProviderActionExpression(type)) { extractProviderActionExpressions($scope.AssociateActionLists); } }); } $scope.showEditor = function (callback, params, ticketObj, contentId) { promptModal = $modal.open({ templateUrl: 'views/admin/screen-configuration/provider-user-prompt.html', windowClass: 'action-blade provider-user-prompt', backdrop: 'custom', resolve: { ticket: ['ticketModel', function (ticketModel) { return ticketModel.getTicket(contentId, ticketObj.resource); }] }, controller: ['$scope', 'ticket', function ($scope, ticket) { var fieldObj = {}, result = {}, promptField; var initMappingFieldDefaultValue = function (field, defaultValue) { if (field.isCheckboxField()) { var cBoxOption = (field.options[0] || {}); if (_.includes(cBoxOption, defaultValue) || ['true', 'checked'].indexOf(defaultValue) !== -1 || defaultValue === 0) { field.setValue(0); } } else if (field.hasDateDataType() || field.hasDateTimeDataType()) { var stringParseDate = moment(defaultValue); if (stringParseDate.isValid()) { field.setValue(stringParseDate.valueOf()); } else { // Can be the case when we try to parse string representation of timestamp // it is worth to try to parse defaultValue to a number and parse var numberParsedDate = moment(+defaultValue); if (numberParsedDate.isValid()) { field.setValue(numberParsedDate.valueOf()); } } } else if (field.hasTimeDataType()) { var amPmReg = new RegExp(/am|pm/, 'ig'); var parseFormat = 'HH:mm'; if (amPmReg.test(defaultValue)) { parseFormat = 'hh:mma'; } var setTimeValue = moment(defaultValue, parseFormat); if (setTimeValue.isValid()) { field.value = setTimeValue.valueOf(); } } else if (field.isDropdownField() || field.isRadioField()) { var defaultValueOption = _.find(field.options, function (option) { return _.includes(option, defaultValue); }); if (defaultValueOption) { field.setValue(defaultValueOption.name); } } else { field.setValue(defaultValue); } }; $scope.fieldArray = params.prompt; // These are required for custom field directives, to render properly $scope.editMode = true; $scope.ticket = ticket; $scope.fieldArray.forEach(function (field) { if (field.defaultValue) { initMappingFieldDefaultValue(field.mappedField, field.defaultValue); } }); $scope.getMappedFieldRenderType = function (field) { var type = 'text'; if (field.isCheckboxField()) { type = 'checkbox'; } else if (field.hasDateDataType()) { type = 'date'; } else if (field.hasDateTimeDataType()) { type = 'datetime'; } else if (field.hasTimeDataType()) { type = 'time'; } else if (field.isNumberField()) { type = 'number'; } else if (field.isDropdownField() || field.isRadioField()) { type = field.options.length > 0 ? 'enum' : 'text'; /*if (type === 'number') { field.max = Infinity; field.min = 0; }*/ } return type; }; $scope.executeAction = function () { var isEmptyPromptField = false, emptyFieldLabel = ''; for (var i in $scope.fieldArray) { promptField = $scope.fieldArray[i]; fieldObj[promptField.mappedFieldId] = getMappedFieldValue(promptField.mappedField); if (fieldObj[promptField.mappedFieldId] === null || fieldObj[promptField.mappedFieldId] === '') { emptyFieldLabel += promptField.mappedFieldName + ' '; isEmptyPromptField = true; } } if (isEmptyPromptField) { var message = emptyFieldLabel + $filter('i18n')('common.labels.nonEmpty'); systemAlertService.error({ text: message, clear: true, hide: 10000 }); } else { result = {}; _.extend(result, params, fieldObj); callback(result, ticketObj, contentId); promptModal.dismiss(); } }; $scope.cancelExecution = function () { isExecutingProviderAction = false; promptModal.dismiss(); executeQueuedProviderAction(ticketObj); }; function handleModalBackdropClick() { $scope.cancelExecution(); } var getMappedFieldValue = function (field) { var value = field.getValue(); if (field.isDropdownField()) { if (field.options.length && !field.ootb && value !== null) { value = _.get(_.find(field.options, { name: value }), 'name'); } } else if (field.isCheckboxField() && value === null) { value = -1; } return value; }; $scope.$on(events.MODAL_BACKDROP_CLICK, handleModalBackdropClick); }], size: 'sm' }); }; $scope.displaypromptModal = function (executeCallback, params, templateId, ticketObj, contentId) { screenConfigurationModel.getTemplateById(templateId).then(function (templateObj) { _.each(params.prompt, function (prompt) { var mid = prompt.mappedFieldId, found = _.find(templateObj.mappings, { mappedFieldId: mid }); if (!_.isUndefined(found)) { prompt.defaultValue = found.defaultValue || ''; prompt.mappedFieldName = found.mappedField.label || found.mappedFieldName; prompt.dataType = found.mappedField.dataType; prompt.mappedField = new FieldVO().build(found.mappedField); prompt.mappedField.hideLabel = true; prompt.mappedField.isReadOnly = false; prompt.mappedField.isRequired = false; prompt.mappedField.isHidden = false; prompt.sequence = found.sequence; } }); params.prompt = _.sortBy(params.prompt, 'sequence'); //fix to show fields as per sequence in template object $scope.showEditor(executeCallback, params, ticketObj, contentId); }); }; $scope.launchAction = function (actionItem, actionType, $event, calledFromActionExpression) { $log.topic('actionExecution', actionItem, actionType); if (actionType === 'client') { //URL action //metadataModel.getMetadataByType(scope.context.type).then(function (metadata) { //regex for finding square brackets [params] in the url var re = new RegExp('\\[\\w+\\]'), url = actionItem.url, target = actionItem.target === 'new' ? '_blank' : '_self'; var findLabelObject = function (labelObject) { return labelObject.name === key; }; while (url.search(re) !== -1) { var match = url.match(re), key = match[0].replace(/[\[\]']+/g, ''); //remove the square brackets //check whether field is available in custom fields first else it should be in metadata if ($scope.context.customFields && angular.isDefined($scope.context.customFields[key])) { var selectionFieldObject = _.find(screenConfigurationModel.fieldLabels, findLabelObject); if (selectionFieldObject && selectionFieldObject.options && (selectionFieldObject.options.length > $scope.context.customFields[key])) { url = url.replace(re, selectionFieldObject.options[$scope.context.customFields[key]].label); } else { url = url.replace(re, $scope.context.customFields[key]); } } else { var valueFromRecord = screenConfigurationService.getContextPropertyByMetadataMapping($scope.context, metadata, key) || '', localizedValue = $filter('localizeLabel')(valueFromRecord, key, $scope.context.type); url = url.replace(re, localizedValue || valueFromRecord); } } $log.topic('actionExecution', 'Launch URL action', url, target); $window.open(encodeURI(url), target); //}); } else if (actionType === 'launch') { $scope.launchActionCallback({ actionItem: actionItem, event: $event }); } else { //Provider action contextId = type === EntityVO.TYPE_ASSET ? $scope.context.reconciliationId : $scope.context.id; if (EntityVO.ENTITIES_WITH_NEW_CUSTOMIZATION.indexOf($scope.context.type) !== -1) { $log.topic('providerExecution', 'New Provider action', actionItem, contextId); if (calledFromActionExpression) { $scope.getInputParams(actionItem, $scope.context, $scope.executeActionFromExpressionCallback, contextId); } else { $scope.getInputParams(actionItem, $scope.context, $scope.executeActionCallback, contextId); } } else { $log.topic('actionExecution', 'Old Provider action', actionItem.id, contextId); screenConfigurationModel.executeProviderAction(actionItem.id, contextId).then(function () { var message = $filter('i18n')('screenConfiguration.providerActionExecuted', actionItem.actionName); systemAlertService.success({ text: message, clear: true, hide: 10000 }); }).catch(function (error) { systemAlertService.error({ text: (error.data && error.data.error) ? error.data.error : error.defaultMessage, clear: false }); }); } } }; $scope.getInputParams = function (actionObj, ticketObj, callback, contextId) { var paramObj = { prompt: [] }, showUserPrompt = false, value, fieldDef, fieldName, obj, i; for (i in actionObj.mappings) { obj = actionObj.mappings[i]; if (obj.type.indexOf('input') === 0) { if (obj.mappedFieldValue && (obj.type.indexOf('ticket') !== -1)) { fieldName = obj.mappedFieldValue; fieldDef = objectValueMapperService.getFieldDefinitionByName(fieldName, obj.mappedFieldId); value = objectValueMapperService.getExactValueByFieldName(fieldName); $log.topic('providerExecution', obj.type, 'fieldName:', fieldName, 'value :', value); if (fieldDef && (fieldDef.isDropdownField() || fieldDef.isRadioField())) { var selectedOption = _.find(fieldDef.options, { name: value }); if (selectedOption) { value = selectedOption.index; } else { value = undefined; } } if (_.isString(value) && fieldDef && (fieldDef.isCheckboxField() || (value === "" && fieldDef.isNumberField()))) { value = undefined; } if (fieldDef && (fieldDef.isDateTimeField() || fieldDef.isDateField()) && !value) { // In order to prevent error from the backend, client should send null instead of empty string for dates value = null; } paramObj[obj.mappedFieldId] = value; $log.topic('providerExecution', obj.type, paramObj); } else if (obj.type.indexOf('prompt') !== -1) { showUserPrompt = true; paramObj.prompt.push(obj); $log.topic('providerExecution', obj.type, paramObj); } else if (obj.type.indexOf('default') !== -1) { if (obj.mappedFieldValue) { paramObj[obj.mappedFieldId] = obj.mappedFieldValue; } else { paramObj[obj.mappedFieldId] = ''; } $log.topic('providerExecution', obj.type, paramObj); } } } if (showUserPrompt) { $scope.displaypromptModal(callback, paramObj, actionObj.templateId, actionObj, contextId); } else { callback(paramObj, actionObj, contextId); $log.topic('providerExecution', actionObj, paramObj); } return paramObj; }; var setOutputParams = function (actionObj, data) { $log.topic('providerExecution', 'setOutputParams', data); if (!_.isEmpty(data)) { $log.topic('providerExecution', 'OPEN_EDIT_MODE'); $rootScope.$broadcast(events.OPEN_EDIT_MODE); //Delaying setting field values, so that field values are set after edit mode is open //Else the field value and originalData value will be the same and cannot reset to old value. $timeout(function () { objectValueMapperService.setFieldFromProvider(data, actionObj.mappings).then(function () { $log.topic('providerExecution', 'Loaded output values'); }); }); } }; $scope.executeActionCallback = function (params, actionItem, contextId) { delete params.prompt; if (actionItem.isSynchronous) { $scope.$emit(events.PROVIDER_SHOW_LOADING); } screenConfigurationModel.executeProviderActionV3(actionItem.id, contextId, params).then(function (data) { setOutputParams(actionItem, data); var message = $filter('i18n')('screenConfiguration.providerActionExecuted', actionItem.label); $scope.$emit(events.PROVIDER_HIDE_LOADING); $log.topic('providerExecution', 'Show success message'); systemAlertService.success({ text: message, clear: true, hide: 10000 }); }).catch(function (error) { $scope.$emit(events.PROVIDER_HIDE_LOADING); systemAlertService.error({ text: (error.data && error.data.error) ? error.data.error : error.defaultMessage, clear: false }); }); }; $scope.executeActionFromExpressionCallback = function (params, actionItem, contextId) { delete params.prompt; if (actionItem.isSynchronous) { $scope.$emit(events.PROVIDER_SHOW_LOADING); } screenConfigurationModel.executeProviderActionV3(actionItem.id, contextId, params).then(function (data) { isExecutingProviderAction = false; setOutputParams(actionItem, data); var message = $filter('i18n')('screenConfiguration.providerActionExecutedFromExpression', actionItem.label); $scope.$emit(events.PROVIDER_HIDE_LOADING); $log.topic('providerExecution', 'Show success message'); systemAlertService.success({ text: message, clear: true, hide: 10000 }); executeQueuedProviderAction(actionItem); }).catch(function (error) { $scope.$emit(events.PROVIDER_HIDE_LOADING); isExecutingProviderAction = false; executeQueuedProviderAction(actionItem); systemAlertService.error({ text: (error.data && error.data.error) ? error.data.error : error.defaultMessage, clear: false }); }); }; function executeQueuedProviderAction(actionItem) { objectValueMapperService.clearProviderActionFieldName(actionItem.actionFieldName, actionItem.actionId); clearProviderAction(actionItem); if ($scope.providerActionsList.length) { $timeout(function () { $scope.launchActionFromExpression($scope.providerActionsList); }); } } $scope.checkForOutputMapping = function (actionObj) { var returnVal = false; if (actionObj && actionObj.mappings && actionObj.mappings.length) { if (!_.isUndefined(_.find(actionObj.mappings, { type: 'output' }))) { returnVal = true; } } return returnVal; }; $scope.hide = function () { promptModal.hide(); }; //whenever ticket is getting loaded or user clicks on cancel button of edit ticket this event is registered causing memory leak as well as firing multiple times. if (!objectValueMapperService.isProviderActionEventRegistered()) { objectValueMapperService.registerProviderActionCallExpressionEvent($rootScope.$on(events.PROVIDER_ACTION, function (event, data) { if (data.length) { $scope.providerActionsList = objectValueMapperService.getProviderActionsList(); if (_.size($scope.providerActionsList) && !isExecutingProviderAction) { $scope.launchActionFromExpression($scope.providerActionsList); } } })); } // $scope.$on('$destroy', function () { // unbindRootScopeListener(); // }); $scope.launchActionFromExpression = function (actionList) { if (!_.isArray(actionList) || (actionList && actionList.length <= 0)) { return; } // the expression may no longer be valid based on the previous actions output mappings, // so re-evaluate expression before executing the next action var value = expressionEvaluatorService.evaluate(actionList[0].expression); if (value) { isExecutingProviderAction = true; $scope.launchAction(actionList[0].action, actionList[0].action.actionType, null, true); } else if (actionList[0] && actionList[0].action) { objectValueMapperService.clearProviderActionFieldName(actionList[0].action.actionFieldName, actionList[0].action.actionId); } }; function clearProviderAction(actionItem) { objectValueMapperService.clearProviderActionFromList(actionItem); } }]); })();