"use strict"; (function () { 'use strict'; angular.module('ticketModule') .factory('ticketModel', ['ticketService', 'relationService', 'relationModel', '$q', 'personModel', 'slaCalculatorService', 'attachmentService', 'categoriesService', 'systemAlertService', '$filter', 'metadataModel', 'userModel', 'smartRecorderModel', 'objectValueMapperService', function (ticketService, relationService, relationModel, $q, personModel, slaCalculatorService, attachmentService, categoriesService, systemAlertService, $filter, metadataModel, userModel, smartRecorderModel, objectValueMapperService) { var ticketModel = { dataCache: {}, cache: {} }, previewCacheInterval = moment.duration(2, 'minutes').valueOf(), changeTaskPhases = []; ticketModel.getTicketDetails = function (id, type) { return ticketService.getBasicInformation(id, type); }; ticketModel.getTicketAttachments = function (id, type) { return EntityVO.hasAttachments(type) ? attachmentService.getAttachmentsInfo(id, type) : $q.when([]); }; ticketModel.calculateRisk = function (releaseid) { return ticketService.getReleaseRisk(releaseid); }; ticketModel.getTicket = function (id, type, ignoreCache) { //Added ticket type check, to make sure that correct item is fetched(WO and Request can have equal id) //Added ignoreCache flag, which will prevent data caching when navigating from Global search preview to full ticket profile if (!ticketModel.cache[id] || (ticketModel.cache[id] && type && (ticketModel.cache[id].type !== type)) || ignoreCache) { ticketModel.cache[id] = $q.all([ticketService.getBasicInformation(id, type), ticketModel.getTicketAttachments(id, type)]).then(function (result) { var ticketVO = result[0]; if (type === EntityVO.TYPE_CHANGE || type === EntityVO.TYPE_RELEASE) { var metadata = metadataModel.cache[type] || {}, documentTypes = metadata.documentTypes; ticketVO.plans = result[1]; documentTypes && _.each(ticketVO.plans, function (plan) { var docType = _.find(documentTypes, { index: plan.workNote.workNoteTypeId }); plan.workNote.documentType = docType; }); } else { ticketVO.attachments = result[1]; } ticketModel.cache[id] = ticketVO; return ticketModel.cache[id]; }); return $q.when(ticketModel.cache[id]); } else { var isAngularPromise = _.has(ticketModel.cache[id], '$$state.status'); return $q.when(isAngularPromise ? ticketModel.cache[id] : angular.copy(ticketModel.cache[id])); } }; function processUploadAttachment(attachments, ticketAttachments, id, type) { var promiseArray = [], promise; if (_.size(attachments.attachmentToUpload)) { if (type === EntityVO.TYPE_TASK) { promise = attachmentService.uploadAttachment(type, id, attachments.attachmentToUpload) .then(function (response) { ticketAttachments = response; }); promiseArray.push(promise); } else { _.each(attachments.attachmentToUpload, function (attachment) { promise = attachmentService.uploadAttachment(type, id, attachment).then(function (response) { if (_.size(response)) { ticketAttachments.push(response[0]); } }); promiseArray.push(promise); }); } } if (promiseArray.length > 0) { return $q.all(promiseArray).then(function (responses) { ticketModel.cache[id].attachments = ticketAttachments; return angular.copy(ticketModel.cache[id]); }).catch(function (error) { delete ticketModel.cache[id]; return error; }); } return ticketModel.cache[id]; } ticketModel.update = function (id, type, data) { var promiseArray = []; return ticketService.update(id, type, data) .then(function (ticketVO) { _.assign(ticketModel.cache[id], ticketVO); var descAttachments = objectValueMapperService.getFieldByName('desc'); var attachments = descAttachments && descAttachments.getValue() || {}; var ticketAttachments = ticketModel.cache[id].attachments ? _.reject(ticketModel.cache[id].attachments, { pendingSave: true }) : []; // process attachments to delete if (_.size(attachments.attachmentToDelete)) { _.each(attachments.attachmentToDelete, function (attachment) { ticketAttachments = _.reject(ticketAttachments, function (ticketAttachment) { return ticketAttachment.attachmentReference.dataSourceId === attachment.attachmentReference.dataSourceId && ticketAttachment.attachmentReference.attachmentId === attachment.attachmentReference.attachmentId; }); promiseArray.push(attachmentService.deleteAttachment(type, { dataSourceId: attachment.attachmentReference.dataSourceId, attachmentId: attachment.attachmentReference.attachmentId })); }); return $q.all(promiseArray).then(function () { // process attachments to upload return processUploadAttachment(attachments, ticketAttachments, id, type); }); } else { // process attachments to upload return processUploadAttachment(attachments, ticketAttachments, id, type); } }).catch(function (error) { return error; }); }; ticketModel.getTicketAssigneePersons = function (type, role, customerCompany, companyName) { return ticketService.getAssigneeSupportGroupPersons(companyName || '', '', '', userModel.userId, role, '', customerCompany, '', '', '', '', true); }; /** * Get Ticket details with async request for ticket relations, used for ticket profile pages. (Data is provided from cache, if exists) * * @param id of the ticket * @param type of the ticket, incident, workorder, task, request * @returns promise, that will be resolved after ticket data is loaded */ ticketModel.getTicketWithAsyncRelations = function (id, type) { relationModel.getRelations(id, type); return ticketModel.getTicket(id, type); }; /** * Get Ticket details preview directives. (Data is provided from cache, if exists and if not expired) * * @param id of the ticket * @param type of the ticket, incident, workorder, task, request * @returns promise, that will be resolved after ticket data is loaded */ ticketModel.getTicketForPreview = function (id, type) { if (ticketModel.cache[id] && ((moment().valueOf() - ticketModel.cache[id].syncTime) > previewCacheInterval)) { delete ticketModel.cache[id]; } return ticketModel.getTicket(id, type); }; /** * Load ticket draft * * @param type * @param templateId * @returns {*} */ ticketModel.getDraft = function (type, templateId, summary, companyName, isCreateNewWO) { if (!_.isEmpty(ticketModel.cache.draft)) { if (!_.isEmpty(ticketModel.cache.relatedDraft)) { var relatedDraft = ticketModel.cache.relatedDraft; if (relatedDraft.fromType === EntityVO.TYPE_ASSET) { return ticketService.getRelatedDraftFromAsset(relatedDraft.type, relatedDraft.fromContext); } else { return ticketService.getDraftForRelated(relatedDraft.type, relatedDraft.fromType, relatedDraft.id); } } return $q.when(ticketModel.cache.draft); } if (type === EntityVO.TYPE_WORKORDER) { return ticketService.getDraftWorkOrder(type, templateId, summary, isCreateNewWO ? null : smartRecorderModel.smartRecorderData); } else { return ticketService.getDraft(type, templateId, summary, companyName); } }; /** * Save ticket draft * * @param type * @param ticket * @returns promise */ ticketModel.saveDraft = function (type, ticket) { if (type === EntityVO.TYPE_INCIDENT) { if (ticket.serviceType) { ticket.serviceType = ticket.serviceType; } } return ticketService.saveDraft(type, ticket) .then(function (ticketJSON) { var resPackage = {}; ticket.isDraft = false; if (!_.isEmpty(ticket.attachments)) { var attachmentsPromiseArray = []; _.each(ticket.attachments, function (attachment) { var attachmentPromise = attachmentService.uploadAttachment(ticket.type, ticketJSON.id, attachment); attachmentsPromiseArray.push(attachmentPromise); }); return $q.all(attachmentsPromiseArray).then(function (response) { resPackage = { attachments: response, ticketData: ticketJSON }; return resPackage; }); } else { resPackage.ticketData = ticketJSON; return resPackage; } }); }; /** * Retrieves full information on Ticket's contact person * @param {String} id Ticket instance unique identifier id */ //TODO this is a temporary method. Full person information should be a part of instance data response ticketModel.getFullPersonData = function (id) { var context = ticketModel.cache[id]; return personModel.getListOfPersonByName(context.customer.firstName, context.customer.lastName) .then(function (listOfPerson) { listOfPerson.length && (context.customer = listOfPerson[0]); }); }; /** * Retrieves information on instance attachments * @param {String} id Ticket instance unique identifier id * @param {String} type Ticket instance type - e.g. task, workorder, incident */ ticketModel.getAttachmentsInfo = function (id, type) { return attachmentService.getAttachmentsInfo(id, type) .then(function (attachments) { return attachments; }).catch(function (error) { return error; }); }; /** * Updates status of a ticket * @param ids Array of items to change state * @param statusData Object with change status params * @returns promise */ ticketModel.updateStatus = function (ids, statusData, type) { return ticketService.updateStatus(ids, statusData, type).catch(function (error) { if (error) { systemAlertService.error({ text: error.data.error, clear: false }); } return $q.reject(error); }); }; /** * Refresh current ticket status * @param id * @param type * @param statusData */ ticketModel.refreshStatus = function (id, type, statusData) { var ticket = ticketModel.cache[id]; var metadata = metadataModel.cache[type]; ticket.status.value = _.find(metadata.statuses, { name: statusData.status }).name; ticket.status.reason = statusData.statusReason; ticket.milestone = statusData.milestone; ticket.resolution = statusData.resNote; if (!_.isEmpty(statusData.updatedTickets)) { var currentTicket = _.find(statusData.updatedTickets, function (updatedTicket) { return updatedTicket.id === id; }); ticket.accessMappings = currentTicket.accessMappings; if (type === EntityVO.TYPE_CHANGE) { ticket.scheduledStartDate = currentTicket.scheduledStartDate ? new Date(currentTicket.scheduledStartDate) : null; ticket.scheduledEndDate = currentTicket.scheduledEndDate ? new Date(currentTicket.scheduledEndDate) : null; ticket.actualStartDate = currentTicket.actualStartDate ? new Date(currentTicket.actualStartDate) : null; ticket.actualEndDate = currentTicket.actualEndDate ? new Date(currentTicket.actualEndDate) : null; ticket.targetDate = currentTicket.targetDate ? new Date(currentTicket.targetDate) : null; ticket.managerGroup = currentTicket.managerGroup; ticket.manager = currentTicket.manager; ticket.isInApproval = currentTicket.isInApproval; //SW00522514 fix if (ticket.isInApproval) { ticket.accessMappings.statusEditAllowed = true; } ticket.status.value = _.find(metadata.statuses, { name: currentTicket.status.value }).name; //backend approval process could have changed the status } else if (type === EntityVO.TYPE_RELEASE) { ticket.scheduledStartDate = currentTicket.scheduledStartDate ? new Date(currentTicket.scheduledStartDate) : null; ticket.scheduledEndDate = currentTicket.scheduledEndDate ? new Date(currentTicket.scheduledEndDate) : null; ticket.actualStartDate = currentTicket.actualStartDate ? new Date(currentTicket.actualStartDate) : null; ticket.actualEndDate = currentTicket.actualEndDate ? new Date(currentTicket.actualEndDate) : null; ticket.targetDate = currentTicket.targetDate ? new Date(currentTicket.targetDate) : null; ticket.isInApproval = currentTicket.isInApproval; //SW00522514 fix if (ticket.isInApproval) { ticket.accessMappings.statusEditAllowed = true; } ticket.status.value = _.find(metadata.statuses, { name: currentTicket.status.value }).name; //backend approval process could have changed the status } else if (type === EntityVO.TYPE_PROBLEM) { ticket.coordinator = currentTicket.coordinator; ticket.coordinatorGroup = currentTicket.coordinatorGroup; ticket.assignee = currentTicket.assignee; ticket.supportGroup = currentTicket.supportGroup; if (ticket.targetDate !== currentTicket.targetDate) { ticket.targetDate = currentTicket.targetDate; ticket.SLA = currentTicket.SLA; ticketModel.refreshSLA(ticket); } } } if (type !== EntityVO.TYPE_OUTAGE) { ticketModel.refreshSLA(ticket); } if (type === EntityVO.TYPE_INCIDENT && ticket.isClosed()) { _.each(statusData.resCategorizations, function (resCat) { var index = _.findIndex(ticket.resCategorizations, { name: resCat.name }); if (index > -1) { ticket.resCategorizations[index] = resCat; } else { ticket.resCategorizations.push(resCat); } }); _.each(statusData.categorizations, function (cat) { var index = _.findIndex(ticket.categorizations, { name: cat.name }); if (index > -1) { ticket.categorizations[index] = cat; } else { ticket.categorizations.push(cat); } }); var allEntityCategories = _.cloneDeep(ticket.categorizations); if (ticket.resCategorizations) { _.each(ticket.resCategorizations, function (resCat) { var index = _.findIndex(ticket.categorizations, { name: resCat.name }); if (index > -1) { ticket.categorizations[index] = resCat; allEntityCategories[index] = resCat; } else { allEntityCategories.push(resCat); } }); } ticket.allCategories = categoriesService.populateCategories(allEntityCategories, metadata); ticket.noCategories = (_.filter(ticket.allCategories, 'valueToShow')).length; } }; /** * Apply Action * * @param id * @param type * @param action * @param data * @returns {*} */ ticketModel.applyAction = function (id, type, action, data) { return ticketService.applyAction(id, type, action, data).catch(function (error) { if (error) { systemAlertService.error({ text: error.data.error, clear: false }); } return $q.reject(error); }); }; /** * Edit header * * @param id * @param type * @param headerData * @returns {*} */ ticketModel.editHeader = function (id, type, headerData) { return ticketService.editHeader(id, type, headerData); }; ticketModel.editCustomerCard = function (id, type, data) { return ticketService.editCustomerCard(id, type, data); }; ticketModel.getPriority = function (companyName, impact, urgency, type) { return ticketService.getPriority(companyName, impact, urgency, type); }; ticketModel.getChangePriority = function (companyName, impact, urgency) { return ticketService.getChangePriority(companyName, impact, urgency); }; ticketModel.getChangeRiskRules = function (companyName) { return ticketService.getChangeRiskRules(companyName); }; ticketModel.getReleasePriority = function (companyName, impact, urgency) { return ticketService.getReleasePriority(companyName, impact, urgency); }; ticketModel.getAvailableStatuses = function (id, type) { return ticketService.getAvailableStatuses(id, type); }; /** * Refresh SLA for ticket * * @param ticket */ ticketModel.refreshSLA = function (ticket) { if (ticket.SLA) { if (ticket.isClosed()) { ticket.resolvedDate = moment().unix() * 1000; } /* force refresh the progress bar */ ticket.SLA.slaProgressBarValid = false; if (ticket.SLA.slaRenderType === 'full') { slaCalculatorService.getAndCalculateSLA(ticket); } else if (ticket.SLA.slaRenderType === 'partial') { slaCalculatorService.calculate(ticket); } } }; ticketModel.groupRelatedItems = function (relatedItems) { _.each(relatedItems, function (relatedItem) { relatedItem.relationshipType = relatedItem.relationshipClassId || relatedItem.relationshipType; relatedItem.relationshipTypeLabel = getRelationshipTypeLabel(relatedItem.relationshipType); }); var sortedItems = _.sortBy(relatedItems, 'relationshipTypeLabel'); var groupped = _.groupBy(sortedItems, 'relationshipTypeLabel'); var result = []; for (var key in groupped) { if (groupped.hasOwnProperty(key)) { result.push({ relationshipType: key, limit: 4, items: groupped[key] }); } } return result; }; ticketModel.showAttachmentPreviewerPopup = function (plan) { attachmentService.showAttachmentPreviewDialog(plan); }; /** * @ngdoc method * @name myitsmApp.ticketModel#getTaskPhases * * @description * Retrieves all phases of change request, which can be set for task relation * @param {String} companyName - Required param, based on which task phases will be retrieved * @param {String} isTemplate - Optional param, which controls response structure. DEPRECATED */ ticketModel.getTaskPhases = function (companyName, isTemplate) { if (!companyName) { return $q.when({ error: 'Missing required parameter' }); } var companyMatch = _.findWhere(changeTaskPhases, { company: companyName }); if (!companyMatch) { var promise = ticketService.getTaskPhases({ companyName: companyName, isTemplate: isTemplate }).then(function (resp) { companyMatch.phases = resp; return resp; }); companyMatch = { phases: promise, company: companyName }; changeTaskPhases.push(companyMatch); } return $q.when(companyMatch.phases); }; /** * @ngdoc method * @name myitsmApp.ticketModel#getTaskPhases * * @description * Retrieves all phases of change request, which can be set for task relation * @param {Object} approval - Object that has information about the ticket required in the request API. */ ticketModel.getFutureTaskPhases = function (companyName, currentPhaseId) { if (!companyName) { return $q.when({ error: 'Missing required parameter' }); } return ticketModel.getTaskPhases(companyName).then(function (response) { var currentPhaseIdx = _.findIndex(response, { guid: currentPhaseId }); return (response || []).slice(currentPhaseIdx); }); }; /** * @ngdoc method * @name myitsmApp.ticketModel#getImpactAnalysisVisualisationData * * @description * Retrieve CIs relation data for impact analysis graph on Change request * @param {Object} change - Data object of the change request, for which impact analysis should be performed */ ticketModel.getImpactAnalysisVisualisationData = function (type, id) { return ticketService.getImpactAnalysisVisualisationData(type, id) .then(function (impactData) { return impactData[0].items[0] || {}; }); }; /** * @ngdoc method * @name myitsmApp.ticketModel#getImpactAnalysisStatus * * @description * Retrieves the status of an impact analysis job for a Change request * @param id - Id of the change request, for which impact analysis should be retrieved * @param type - Type - which is 'change' */ ticketModel.getImpactAnalysisStatus = function (id, type) { return ticketService.getImpactAnalysisStatus(id, type) .then(function (impactAnalysisStatus) { return impactAnalysisStatus; }); }; /** * @ngdoc method * @name myitsmApp.ticketModel#impactAnalysisAction * * @description * Start, Cancel, Force_start, Dismiss an impact analysis job * @param id - Id of the change request, for which impact analysis should be performed/ running * @param type - Type - which is 'change' * @param {Object} params - Object with Command as either start, cancel, force_start or dismiss */ ticketModel.impactAnalysisAction = function (id, type, params) { if (id && type) { return ticketService.impactAnalysisAction(id, type, params) .then(function (response) { return response; }) .catch(function (error) { if (error) { systemAlertService.error({ text: (error.data && error.data.error) || error, clear: true }); } }); } }; ticketModel.getURLforTaskFlow = function (id, params) { if (id) { return ticketService.getURLforTaskFlow(id, params) .then(function (response) { window.open(response.url, '_blank'); return response; }) .catch(function (error) { if (error) { systemAlertService.error({ text: (error.data && error.data.error) || error, clear: true }); } }); } }; ticketModel.saveImpactedAreas = function (id, type, impactedAreas) { var prom; if (impactedAreas && impactedAreas.length > 0) { prom = ticketService.saveImpactedAreas(id, type, impactedAreas); prom.catch(function (error) { if (error) { systemAlertService.error({ text: (error.data && error.data.defaultMessage) || (error.data && error.data.error) || error, clear: true }); } }); return prom; } return $q.when(1); }; ticketModel.deleteImpactedAreas = function (id, type, impactedAreas) { if (impactedAreas && impactedAreas.length > 0) { return ticketService.deleteImpactedAreas(id, type, impactedAreas); } return $q.when(1); }; /** * Retrieves root cause for PBI and PKE based on categories * @params {object} List of company, operational and product categories */ ticketModel.getRootCause = function (category, company) { var params = { company: company, depends: category.depends }; return ticketService.getRootCause(params); }; /** * Retrieves recently used change templates * @params array of templates in localstore */ ticketModel.getRecentlyUsedChangeTemplates = function (templates, metadata) { var changeMetadata = metadata, templateList = []; _.each(templates, function (item) { templateList.push({ id: item.id, company: item.company, name: item.name }); }); return ticketService.getRecentlyUsedChangeTemplates(templateList).then(function (response) { var templateList = response[0].items[0].objects; return _.map(templateList, function (item) { return parseChangeTemplateItem(item, changeMetadata); }); }); }; /** * Retrieves recently used release templates * @params array of templates in localstore */ ticketModel.getRecentlyUsedReleaseTemplates = function (templates, metadata) { var releaseMetadata = metadata, templateList = []; _.each(templates, function (item) { templateList.push({ id: item.id, company: item.company, name: item.name }); }); return ticketService.getRecentlyUsedReleaseTemplates(templateList).then(function (response) { var templateList = response[0].items[0].objects; return _.map(templateList, function (item) { return parseReleaseTemplateItem(item, releaseMetadata); }); }); }; /** * Retrieves vendor information for a ticket based on ticket type and ticket display id * @param displayId - display id of the ticket */ ticketModel.getVendorInfo = function (displayId, type) { return ticketService.getVendorInfo(displayId, type) .then(function (vendorInfo) { _.each(vendorInfo, function (vendorTicket) { if (vendorTicket && _.isString(vendorTicket.vendorTicketUrl)) { vendorTicket.vendorTicketUrl = decodeURIComponent(vendorTicket.vendorTicketUrl); } }); return vendorInfo; }); }; /** * Transform raw data into change template item. * * @param {Object} data * @returns {ChangeTemplateVO} change template item */ function parseChangeTemplateItem(data, changeMetadata) { var changeTemplateItem = new ChangeTemplateVO().build(data); changeTemplateItem.allCategories = categoriesService.populateCategories(changeTemplateItem.categorizations, changeMetadata); return changeTemplateItem; } /** * Transform raw data into release template item. * * @param {Object} data * @returns {ReleaseTemplateVO} release template item */ function parseReleaseTemplateItem(data, releaseMetadata) { var releaseTemplateItem = new ReleaseTemplateVO().build(data); releaseTemplateItem.allCategories = categoriesService.populateCategories(releaseTemplateItem.categorizations, releaseMetadata); return releaseTemplateItem; } function getRelationshipTypeLabel(value) { var resourceKey = 'common.relationship.type.' + value, label = $filter('i18n')(resourceKey); if (label !== resourceKey) { return label; } else { return value; } } return ticketModel; } ]); })();