"use strict"; /** * Created by andey on 14-11-2016. */ (function () { 'use strict'; angular.module('releaseModule') .controller('CreateReleaseController', ['$scope', '$rootScope', '$state', '$q', '$http', '$modal', '$timeout', 'releaseTabIds', 'userModel', 'searchModel', 'metadataModel', 'ticketModel', 'createTicketModel', 'ticketActionService', 'categoriesService', 'editTicketDatesService', 'ticketTemplateModel', 'ticketService', 'systemAlertService', 'events', 'localStorageService', 'i18nService', 'configurationModel', '$filter', 'feedModel', 'utilityFunctions', function ($scope, $rootScope, $state, $q, $http, $modal, $timeout, releaseTabIds, userModel, searchModel, metadataModel, ticketModel, createTicketModel, ticketActionService, categoriesService, editTicketDatesService, ticketTemplateModel, ticketService, systemAlertService, events, localStorageService, i18nService, configurationModel, $filter, feedModel, utilityFunctions) { $state.go('createRelease.selector'); $scope.tabIds = releaseTabIds; $scope.draftTicket = { type: EntityVO.TYPE_RELEASE }; $scope.selections = { companies: [] }; $scope.siteOptions = { company: { visible: false, attribute: 'companyName' }, region: { attribute: 'region' }, siteGroup: { attribute: 'siteGroup' }, site: { attribute: 'name' } }; $scope.validator = editTicketDatesService; $scope.releasePlanMilestoneDirty = false; $scope.createDraftRelease = function () { $scope.state.dataIsLoading = true; $scope.draftCreated = false; $scope.isNew = true; var templateId = null; if ($scope.selectedMainTab !== $scope.tabIds.main.scratch) { templateId = $scope.template.selected.id; } getDraftPromise(templateId).then(function (response) { $scope.state.dataIsLoading = false; var isTemplate = !_.isEmpty(templateId); objectifyDraftTicket(response, isTemplate); $scope.template.selected.forCompany = $scope.draftTicket.company.name; if ($scope.template.selected && $scope.template.selected.id) { addToLastUsedTemplates($scope.template.selected); } $scope.draftCreated = true; goToWizard(); }) .catch(function (error) { $scope.state.dataIsLoading = false; error = error.data.detailedMessage || error.data.error || error; systemAlertService.error({ text: error, clear: true }); }); }; $scope.setCompany = function (company) { $scope.draftTicket.company = company; $scope.showRecentTemplates = true; getlastUsedTemplates(company.name); }; $scope.selectMainTab = function (tab) { $scope.selectedMainTab = tab; }; $scope.releaseWizardValid = function () { return $scope.invalidFormCount() === 0; }; $scope.invalidFormCount = function () { var count = 0; _.forOwn($scope.tabIds.wizard, function (value, tabId) { if (!$scope.formValid(tabId)) { count++; } }); return count; }; $scope.isValidSummary = function () { if ($scope.draftTicket.summary && $scope.draftTicket.summary.length > 0) { return true; } return false; }; $scope.formValid = function (tabId) { return $scope[tabId] ? !$scope[tabId].$invalid : true; }; $scope.formDirty = function (tabId) { return $scope[tabId] ? $scope[tabId].$dirty : false; }; $scope.dirtyFormCount = function () { var count = 0; for (var tabId in $scope.tabIds.wizard) { if ($scope.formDirty(tabId)) { count++; } } return count; }; $scope.releaseWizardDirty = function () { return $scope.dirtyFormCount() !== 0 || !_.isEmpty($scope.draftTicket.documents); }; $scope.$on(events.RELEASE_WIZARD_FORM_STATE, function (event, form) { $scope[form.name] = $scope[form.name] || {}; if (typeof form.invalid !== 'undefined') { $scope[form.name].$invalid = form.invalid; } if (typeof form.dirty !== 'undefined') { $scope[form.name].$dirty = form.dirty; } }); $scope.clear = function () { if ($scope.releaseWizardDirty()) { var modalInstance = displayDirtyFormAlert(); modalInstance.result.then(function (data) { if (data) { reset(); } }); } else { reset(); } }; $scope.clearTemplateSearch = function () { $scope.template = { search: '', list: [], selected: {}, showSearchResults: false, preview: null }; }; $scope.openDatePicker = function (calendar, event) { calendar.open = true; event.stopPropagation(); }; $scope.updatePriority = function () { $scope.state.dataIsLoading = true; ticketModel.getReleasePriority($scope.draftTicket.company.name, $scope.draftTicket.impact.name, $scope.draftTicket.urgency.name).then(function (data) { $scope.draftTicket.priority = _.find($scope.releaseMetadata.priorities, { name: data }); if (!$scope.draftTicket.priority) { systemAlertService.error({ text: $filter('i18n')('create.change.wizard.basicDetails.priority.warning'), hide: 10000 }); } $scope.state.dataIsLoading = false; }); }; $scope.nextStep = function () { var takeNext = false; _.forOwn($scope.tabIds.wizard, function (value, tabId) { if (takeNext) { takeNext = false; $scope.state.selectedWizardTab = tabId; return; } if (value === $scope.state.selectedWizardTab) { takeNext = true; } }); }; $scope.isDocumentTab = function () { return $scope.state.selectedWizardTab === $scope.tabIds.wizard.documents; }; $scope.getTemplateList = function (query) { return ticketTemplateModel.getReleaseTemplateList(query, $scope.draftTicket.company.name, $scope.releaseMetadata).then(function (data) { $scope.state.exceedsChunkSizeTemplates = data.exceedsChunkSize; $scope.state.isTooltipOpenTemplates = data.exceedsChunkSize; $timeout(function () { $scope.state.isTooltipOpenTemplates = false; }, 10000); return _.uniqBy(_.map(data.items, 'name')); }); }; $scope.getRecommendedTemplates = function () { if (_.isEmpty($scope.template.search)) { return; } $scope.state.templatesLoading = true; ticketTemplateModel.getReleaseTemplateList($scope.template.search, $scope.draftTicket.company.name, $scope.releaseMetadata).then(function (data) { $scope.template.list = data.items; }).finally(function () { $scope.state.templatesLoading = false; $scope.template.showSearchResults = true; }); }; $scope.getList = function (type, term) { return createTicketModel.getList(type, term).then(function (response) { if (type === 'service') { $scope.state.exceedsChunkSizeService = response.exceedsChunkSize; } $scope.state.isTooltipOpenService = $scope.state.exceedsChunkSizeService; $timeout(function () { $scope.state.isTooltipOpenService = false; }, 10000); return response.objects; }); }; $scope.onInputFocusBlur = function () { $scope.state.isTooltipOpenService = false; $scope.state.isTooltipOpenTemplates = false; }; $scope.getCompaniesByName = function (name) { return searchModel.getCompaniesByText(name).then(function (response) { return { list: response.companies, exceedsChunkSize: response.exceedsChunkSize }; }); }; $scope.createBlankActivity = function () { isReleasePlanDirty(function (status) { if (status) { continueWithActivityCreation(); } }); function continueWithActivityCreation() { ticketModel.activityParent = $scope.draftTicket; var modalInstance = $modal.open({ templateUrl: 'views/create/create-activity.html', controller: 'CreateActivityController', windowClass: 'ticket__open-modal', backdrop: false, keyboard: false }); modalInstance.result.then(function () { refreshRelations(); createTicketModel.currentTicket = $scope.draftTicket; }); } }; $scope.browseActivityTemplate = function () { isReleasePlanDirty(function (status) { if (status) { continueBrowsingTemplate(); } }); function continueBrowsingTemplate() { ticketModel.activityParent = $scope.draftTicket; var modalInstance = $modal.open({ templateUrl: 'views/template/browse-ticket-template-action-blade.html', windowClass: 'action-blade', controller: 'BrowseTicketTemplateController', resolve: { ticketType: function () { return EntityVO.TYPE_ACTIVITY; }, metadata: function () { return {}; }, company: function () { return $scope.draftTicket.company; } } }); modalInstance.result.then(function () { refreshRelations(); }); } }; $scope.createRelease = function () { isReleasePlanDirty(function (status) { if (status) { $scope.state.savingRelease = true; $scope.draftTicket.summary = $scope.draftTicket.summary.replace(/\u00a0/g, ' '); $scope.draftTicket.categorizations = categoriesService.collectValues($scope.draftTicket.allCategories, $scope.draftTicket, null, true).categorizations; if ($rootScope.timezone) { $scope.draftTicket.scheduledStartDate = !$scope.draftTicket.scheduledStartDate ? $scope.draftTicket.scheduledStartDate : utilityFunctions.convertToUserPreferenceTimezone($scope.draftTicket.scheduledStartDate, $rootScope.timezone); $scope.draftTicket.scheduledEndDate = !$scope.draftTicket.scheduledEndDate ? $scope.draftTicket.scheduledEndDate : utilityFunctions.convertToUserPreferenceTimezone($scope.draftTicket.scheduledEndDate, $rootScope.timezone); $scope.draftTicket.deploymentStartDate = !$scope.draftTicket.deploymentStartDate ? $scope.draftTicket.deploymentStartDate : utilityFunctions.convertToUserPreferenceTimezone($scope.draftTicket.deploymentStartDate, $rootScope.timezone); $scope.draftTicket.deploymentEndDate = !$scope.draftTicket.deploymentEndDate ? $scope.draftTicket.deploymentEndDate : utilityFunctions.convertToUserPreferenceTimezone($scope.draftTicket.deploymentEndDate, $rootScope.timezone); } $scope.$broadcast(events.SAVE_TICKET_DRAFT); createTicketModel.createRelease() .then(function (release) { $q.all(createTicketModel.saveDocuments(createTicketModel.currentTicket)).finally(function () { release.type = EntityVO.TYPE_RELEASE; processPendingWorkNoteFor(release).finally(function () { $scope.state.savingRelease = false; $state.go(EntityVO.TYPE_RELEASE, { id: release.id }); }); }); }) .catch(function (error) { $scope.state.savingRelease = false; error = error.data.detailedMessage || error.data.error || error; systemAlertService.error({ text: error, clear: true }); }); } }); }; $scope.assign = function ($event) { var currentAssignment = { supportGroup: $scope.draftTicket.coordinatorGroup, assignee: $scope.draftTicket.coordinator, company: $scope.draftTicket.company, locationCompany: $scope.draftTicket.company, type: EntityVO.TYPE_RELEASE }; ticketActionService.showAssignDialog(currentAssignment, true).result.then(function (result) { if (result.autoAssign) { $scope.draftTicket.autoAssignAssignee = true; $scope.draftTicket.coordinator = {}; $scope.draftTicket.coordinatorGroup = {}; } else { $scope.draftTicket.autoAssignAssignee = false; if (result.assignee) { $scope.draftTicket.coordinator = result.assignee; } if (result.group) { $scope.draftTicket.coordinatorGroup = result.group; } } $scope[releaseTabIds.wizard.basics].$dirty = true; $event.currentTarget.focus(); }, function () { $event.currentTarget.focus(); }); }; $scope.assignToMe = function ($event, role) { var assigneeRole = role; var assigneeSupportGroup = $scope.draftTicket.coordinatorGroup; var locationCompany = null, customerCompany, primaryCompany = $scope.draftTicket.company; primaryCompany = primaryCompany ? primaryCompany.name : ''; customerCompany = ($scope.draftTicket.customer && $scope.draftTicket.customer.company) ? $scope.draftTicket.customer.company.name : ($scope.draftTicket.company ? $scope.draftTicket.company.name : ''); locationCompany = $scope.draftTicket.location && $scope.draftTicket.location.companyName ? $scope.draftTicket.location.companyName : null; ticketModel.getTicketAssigneePersonsBySearchText(assigneeRole, customerCompany, locationCompany, null, primaryCompany, null).then(function (persons) { persons = _.uniqBy(persons.results, function (person) { return person.loginId && person.supportGroupId; }); var person = persons.length === 1 ? persons[0] : _.find(persons, { supportGroupId: assigneeSupportGroup.id }); if (person && person.loginId) { updateAssignment(person); } else { handleAssignment($event, role); } }); }; $scope.calculateRisk = function (riskLevelSelectionMode) { if (riskLevelSelectionMode === 'auto') { ticketService.getReleaseRisk($scope.draftTicket.id).then(function (response) { if (response.riskLevel === null) { $scope.draftTicket.isCalculateRiskNull = true; } var index = _.findIndex($scope.releaseMetadata.riskLevels, { name: response.riskLevel }); if (index !== -1) { $scope.draftTicket.titleRiskLevelCls = 'risk-level-' + (index + 1); $scope.draftTicket.riskLevel = _.find($scope.releaseMetadata.riskLevels, { name: response.riskLevel }); } else if (!$scope.draftTicket.riskLevel) { $scope.draftTicket.titleRiskLevelCls = 'risk-level-1'; $scope.draftTicket.riskLevel = $scope.releaseMetadata.riskLevels[0]; } }); } else { $scope.draftTicket.isCalculateRiskNull = false; } }; $scope.riskLevel = function () { return $scope.draftTicket.riskLevel ? $scope.draftTicket.riskLevel.label : ''; }; $scope.addExistingChange = function ($event) { var modalInstance = $modal.open({ templateUrl: 'views/release/relate-change-action-blade.html', windowClass: 'action-blade-aif', controller: 'RelateChangeController', resolve: { linkParams: function () { var sequence = 1; if ($scope.draftTicket.noOfRelatedItems[$scope.draftTicket.milestone.name]) { sequence = $scope.draftTicket.noOfRelatedItems[$scope.draftTicket.milestone.name] + 1; } return { selectedItems: [{ id: $scope.draftTicket.id, type: EntityVO.TYPE_RELEASE, desc: $scope.draftTicket.summary, displayId: $scope.draftTicket.displayId, milestone: $scope.draftTicket.milestone }], isDraft: true, sequence: sequence }; } } }); modalInstance.result.then(function () { refreshRelations(); }).finally(function () { $event.currentTarget.focus(); }); }; $scope.$watch('draftTicket.company', function () { if ($scope.draftTicket.company) { $scope.template = { search: '', list: [], selected: {}, showSearchResults: false, preview: null }; } }); function isReleasePlanDirty(callback) { if ($scope.releasePlanMilestoneDirty) { var modalInstance = displayDirtyFormAlert(); modalInstance.result.then(function (data) { if (data) { callback(true); } else { callback(false); } }); } else { callback(true); } } function displayDirtyFormAlert() { var modalInstance = systemAlertService.modal({ title: i18nService.getLocalizedString('common.notification.dirty.title'), text: i18nService.getLocalizedString('common.notification.dirty.message'), buttons: [ { text: i18nService.getLocalizedString('common.labels.yes'), data: true }, { text: i18nService.getLocalizedString('common.labels.no') } ] }); return modalInstance; } function addToLastUsedTemplates(template) { $scope.lastUsedTemplates.unshift(template); $scope.lastUsedTemplates = _.uniqBy($scope.lastUsedTemplates, function (item) { return item.id; }).splice(0, 50); localStorageService.set('release.lastUsedTemplates', $scope.lastUsedTemplates); } function getlastUsedTemplates(company) { $scope.state.lastUsedTemplatesLoading = true; $scope.currentRecentlyUsedTemplates = []; var templates = _.filter($scope.lastUsedTemplates, { forCompany: company }); if (templates.length) { ticketModel.getRecentlyUsedReleaseTemplates(templates, $scope.releaseMetadata).then(function (newTemplates) { $scope.state.lastUsedTemplatesLoading = false; if (newTemplates) { $scope.currentRecentlyUsedTemplates = modifyTemplateAttributes(newTemplates); } }).catch(function () { $scope.state.lastUsedTemplatesLoading = false; }); } else { $scope.state.lastUsedTemplatesLoading = false; } } function modifyTemplateAttributes(templates) { var attributes = ['riskLevel', 'impact', 'urgency', 'releaseType', 'impactedService']; _.forEach(templates, function (template) { _.forEach(attributes, function (attr) { if (!_.isUndefined(template.templateObject[attr])) { template[attr] = template.templateObject[attr]; } }); }); return templates; } function handleAssignment($event, role) { var currentAssignment = { supportGroup: $scope.draftTicket.coordinatorGroup, assignee: $scope.draftTicket.coordinator, locationCompany: $scope.draftTicket.company, type: EntityVO.TYPE_RELEASE }; ticketActionService.showAssignDialog(currentAssignment, true, role).result.then(function (result) { completeAssignment(result); $event.currentTarget.focus(); }, function () { $event.currentTarget.focus(); }); } function updateAssignment(person) { var response = {}; response.assignee = { id: person.id, loginId: person.loginId, fullName: person.fullName, thumbnailMime: person.thumbnailMime, thumbnail: person.thumbnail }; response.group = { id: person.supportGroupId, name: person.supportGroup, organization: person.organization, company: person.company }; completeAssignment(response); } function completeAssignment(result) { if (result.autoAssign) { $scope.draftTicket.autoAssignAssignee = true; $scope.draftTicket.coordinator = {}; $scope.draftTicket.coordinatorGroup = {}; } else { $scope.draftTicket.autoAssignAssignee = false; if (result.assignee) { $scope.draftTicket.coordinator = result.assignee; } if (result.group) { $scope.draftTicket.coordinatorGroup = result.group; } } } function refreshRelations() { //TODO: Code to refresh activities and CRQ related to release. $timeout(function () { $scope.$broadcast(events.REFRESH_RELEASE_PLAN_LIST); }, 1000); } function getDraftPromise(templateId) { return ticketService.getDraft(EntityVO.TYPE_RELEASE, templateId); } function getLocationAsPerCompanySet() { if ($scope.draftTicket.company.name === userModel.userFullData.company.name) { return userModel.userFullData.company.site; } else { return {}; } } function objectifyDraftTicket(response, isTemplate) { if (!isTemplate || !response.company || response.company.name !== $scope.draftTicket.company.name || _.isEmpty(response.location)) { response.location = getLocationAsPerCompanySet(); } response.company = $scope.draftTicket.company; $scope.draftTicket = angular.extend($scope.draftTicket, response); createTicketModel.currentTicket = $scope.draftTicket; $scope.draftTicket.impact = $scope.draftTicket.impact ? _.head(_.filter($scope.releaseMetadata.impacts, { name: $scope.draftTicket.impact })) : _.last($scope.releaseMetadata.impacts); $scope.draftTicket.urgency = $scope.draftTicket.urgency ? _.head(_.filter($scope.releaseMetadata.urgencies, { name: $scope.draftTicket.urgency })) : _.last($scope.releaseMetadata.urgencies); initPriority(); $scope.draftTicket.businessJustification = $scope.draftTicket.businessJustification ? _.head(_.filter($scope.releaseMetadata.businessJustifications, { name: $scope.draftTicket.businessJustification })) : (_.includes($scope.releaseMetadata && $scope.releaseMetadata.requiredOOTBObjectName, 'businessJustification') ? _.head($scope.releaseMetadata.businessJustifications) : null); $scope.draftTicket.releaseType = $scope.draftTicket.releaseType ? _.head(_.filter($scope.releaseMetadata.types, { name: $scope.draftTicket.releaseType })) : null; if ($scope.draftTicket.location) { $scope.draftTicket.location.companyName = $scope.draftTicket.company.name; } if ($scope.draftTicket.riskLevel) { $scope.draftTicket.riskLevel = _.head(_.filter($scope.releaseMetadata.riskLevels, { name: $scope.draftTicket.riskLevel })); $scope.draftTicket.riskLevelSelectionMode = 'manual'; } else { $scope.draftTicket.riskLevelSelectionMode = 'auto'; $scope.calculateRisk($scope.draftTicket.riskLevelSelectionMode); } $scope.draftTicket.allCategories = _.cloneDeep(categoriesService.populateCategories($scope.draftTicket.categorizations, $scope.releaseMetadata)); } function initPriority() { if ($scope.draftTicket.priority) { $scope.draftTicket.priority = _.head(_.filter($scope.releaseMetadata.priorities, { name: $scope.draftTicket.priority })); } else { var lowestImpact = _.last($scope.releaseMetadata.impacts); var lowestUrgency = _.last($scope.releaseMetadata.urgencies); if ($scope.draftTicket.impact === lowestImpact && $scope.draftTicket.urgency === lowestUrgency) { $scope.draftTicket.priority = _.last($scope.releaseMetadata.priorities); } else { $scope.draftTicket.priority = {}; // implement function to fetch priority from backend :: $scope.updatePriority(); } } } function goToWizard() { $state.go('createRelease.wizard'); } function reset() { $scope.selectedMainTab = $scope.tabIds.main.template; $scope.state.selectedWizardTab = $scope.tabIds.wizard.basics; $scope.datePickerOptions = { startingDay: configurationModel.getWeekStartingDay(), 'show-weeks': false, minDate: moment().year(1970).month(0).date(2), maxDate: moment().year(2038).month(0).date(18) }; $scope.template = { search: '', list: [], selected: {}, showSearchResults: false, preview: null }; $scope.draftTicket = { type: EntityVO.TYPE_RELEASE, scheduledStartDatePicker: { open: false }, scheduledEndDatePicker: { open: false }, deploymentStartDatePicker: { open: false }, deploymentEndDatePicker: { open: false } }; $scope.draftCreated = false; $scope.showRecentTemplates = false; $scope.setCompany(userModel.userFullData.company); $state.go('createRelease.selector'); } function processPendingWorkNoteFor(ticket) { return feedModel.processPendingWorkNote(ticket) || $q.when(1); } function init() { $scope.state = { dataIsLoading: true, templatesLoading: false, tooManyCompanies: false, savingRelease: false, lastUsedTemplatesLoading: true }; var storedTemplates = localStorageService.get('release.lastUsedTemplates'); $scope.lastUsedTemplates = storedTemplates ? storedTemplates : []; $scope.releaseMetadata = {}; var metadataPromise = metadataModel.getMetadataByType(EntityVO.TYPE_RELEASE).then(function (metadata) { angular.extend($scope.releaseMetadata, metadata); if (!$scope.releaseMetadata.documentTypes || $scope.releaseMetadata.documentTypes && $scope.releaseMetadata.documentTypes.length === 0) { // todo : we need move from mocks to constant for both release and change case. $http.get('mocks/change-request-metadata-document-types.json') .then(function (data) { $scope.releaseMetadata.documentTypes = data.documentTypes; }); } }); var userPromise = userModel.getFullCurrentUserData().then(function () { $scope.loggedInUserId = userModel.decodedUserId || userModel.userId; $scope.availableForAssignment = userModel.userFullData.availableForAssignment; searchModel.getOperatingCompanies(null, -1).then(function (response) { $scope.selections.companies = _.cloneDeep(response.companies); $scope.state.tooManyCompanies = response.exceedsChunkSize; }); }); $q.all([metadataPromise, userPromise]).then(function () { $scope.state.dataIsLoading = false; reset(); }); } $scope.$on(events.RELEASE_PLAN_MILESTONE_CHANGE, function () { $scope.releasePlanMilestoneDirty = true; }); $scope.$on(events.RELEASE_PLAN_MILESTONE_NOT_DIRTY, function () { $scope.releasePlanMilestoneDirty = false; }); $scope.$watch('draftTicket.impactedService', function () { if ($scope.draftTicket && _.isObject($scope.draftTicket.impactedService)) { $scope.$emit(events.AFFECTED_SERVICE_UPDATED, $scope.draftTicket.impactedService); } }); init(); }]); })();