import org.gnumims.RichUiCalendarItem /** * Service class that encapsulates the business logic for Task Reports. */ class TaskReportService { boolean transactional = false def authService def dateUtilService // def messageSource def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib() def paramsMax = 100000 /** * Selects and returns the reactive ratio. * @param params The request params, may contain params to specify the search. * @param locale The locale to use when generating result.message. */ def getReactiveRatio(params, locale) { def result = [:] def fail = { Map m -> result.error = [ code: m.code, args: [] ] return result } def namedParams = [:] namedParams.startDate = params.startDate ?: dateUtilService.today namedParams.endDate = params.endDate ?: dateUtilService.today // Auto swap date range. if(namedParams.startDate > namedParams.endDate) { def tempStartDate = namedParams.startDate namedParams.startDate = namedParams.endDate namedParams.endDate = tempStartDate } result.startDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.startDate) result.endDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.endDate) namedParams.endDate++ // Start of next day required. namedParams.immediateCallout = TaskType.read(1) namedParams.unscheduledBreakin = TaskType.read(2) namedParams.preventativeMaintenance = TaskType.read(4) namedParams.notStarted = TaskStatus.read(1) result.taskQuery = "from Task as task \ where (task.trash = false \ and task.taskStatus != :notStarted \ and task.targetStartDate < :endDate \ and task.targetStartDate >= :startDate \ and ( \ task.taskType = :immediateCallout \ or task.taskType = :unscheduledBreakin \ or task.taskType = :preventativeMaintenance \ ) \ )" result.taskQuery = "select distinct task " + result.taskQuery result.taskList = Task.executeQuery(result.taskQuery, namedParams) result.taskCount = result.taskList.size() // Assets on Tasks Count. result.totalAssetsOnTasksCount = 0 result.immediateCalloutCount = 0 result.unscheduledBreakinCount = 0 result.preventativeMaintenanceCount = 0 // Summary Of Calculations. result.summaryOfCalculationMethod = 'HQL query: \n\n' def tempStringArray = result.taskQuery.split(' ') tempStringArray.each() { if(it != '') result.summaryOfCalculationMethod += it +'\n' } result.summaryOfCalculationMethod += '\n'+'Calculations: '+'\n\n' result.summaryOfCalculationMethod += 'totalAssetsOnTasksCount = A count of unique assets on each task. \n' result.taskList.each() { task -> if(task.primaryAsset) { result.totalAssetsOnTasksCount++ if(task.taskType == namedParams.immediateCallout) result.immediateCalloutCount++ if(task.taskType == namedParams.unscheduledBreakin) result.unscheduledBreakinCount++ if(task.taskType == namedParams.preventativeMaintenance) result.preventativeMaintenanceCount++ } task.associatedAssets.each() { associatedAsset -> if(associatedAsset.id != task.primaryAsset?.id) { result.totalAssetsOnTasksCount++ if(task.taskType == namedParams.immediateCallout) result.immediateCalloutCount++ if(task.taskType == namedParams.unscheduledBreakin) result.unscheduledBreakinCount++ if(task.taskType == namedParams.preventativeMaintenance) result.preventativeMaintenanceCount++ } } } // each() task // Percentage of counts. result.immediateCalloutPercentage = 0 result.totalPreventativePercentage = 0 result.summaryOfCalculationMethod += 'totalPreventativeCount = unscheduledBreakinCount + preventativeMaintenanceCount\n' result.totalPreventativeCount = result.unscheduledBreakinCount + result.preventativeMaintenanceCount try { result.summaryOfCalculationMethod += 'immediateCalloutPercentage = (immediateCalloutCount / totalAssetsOnTasksCount)*100 \n' result.summaryOfCalculationMethod += 'totalPreventativePercentage = (totalPreventativeCount / totalAssetsOnTasksCount)*100 \n' result.immediateCalloutPercentage = (result.immediateCalloutCount / result.totalAssetsOnTasksCount)*100 result.totalPreventativePercentage = (result.totalPreventativeCount / result.totalAssetsOnTasksCount)*100 } catch(ArithmeticException e) { log.info "Could not calculate: Assets on Tasks Percentages: "+e } // Work Done. result.immediateCalloutWorkDone = [total:0, hours:0, minutes:0, percentage: new BigDecimal(0)] result.unscheduledBreakinWorkDone = [total:0, hours:0, minutes:0] result.preventativeMaintenanceWorkDone = [total:0, hours:0, minutes:0] result.totalPreventativeWorkDone = [total:0, hours:0, minutes:0, percentage: new BigDecimal(0)] result.totalWorkDone = [total:0, hours:0, minutes:0] result.taskList.each() { task -> task.entries.each() { entry -> // Has assets assigned and is Work Done. if( (task.primaryAsset || task.associatedAssets) && entry.entryType.id == 3L ) { if(task.taskType == namedParams.immediateCallout) result.immediateCalloutWorkDone.total += (entry.durationHour*60) + entry.durationMinute if(task.taskType == namedParams.unscheduledBreakin) result.unscheduledBreakinWorkDone.total += (entry.durationHour*60) + entry.durationMinute if(task.taskType == namedParams.preventativeMaintenance) result.preventativeMaintenanceWorkDone.total += (entry.durationHour*60) + entry.durationMinute } } // each() entry } // each() task // Work Done hours and minutes. result.immediateCalloutWorkDone.hours = (result.immediateCalloutWorkDone.total / 60).toInteger() result.immediateCalloutWorkDone.minutes = result.immediateCalloutWorkDone.total % 60 result.unscheduledBreakinWorkDone.hours = (result.unscheduledBreakinWorkDone.total / 60).toInteger() result.unscheduledBreakinWorkDone.minutes = result.unscheduledBreakinWorkDone.total % 60 result.preventativeMaintenanceWorkDone.hours = (result.preventativeMaintenanceWorkDone.total / 60).toInteger() result.preventativeMaintenanceWorkDone.minutes = result.preventativeMaintenanceWorkDone.total % 60 // Work Done Totals. result.totalPreventativeWorkDone.total = result.unscheduledBreakinWorkDone.total + result.preventativeMaintenanceWorkDone.total result.totalPreventativeWorkDone.hours = (result.totalPreventativeWorkDone.total / 60).toInteger() result.totalPreventativeWorkDone.minutes = result.totalPreventativeWorkDone.total % 60 result.totalWorkDone.total = result.immediateCalloutWorkDone.total + result.totalPreventativeWorkDone.total result.totalWorkDone.hours = (result.totalWorkDone.total / 60).toInteger() result.totalWorkDone.minutes = result.totalWorkDone.total % 60 // Work Done Percentages. try { result.immediateCalloutWorkDone.percentage = (BigDecimal)(result.immediateCalloutWorkDone.total / result.totalWorkDone.total)*100 result.totalPreventativeWorkDone.percentage = (BigDecimal)(result.totalPreventativeWorkDone.total / result.totalWorkDone.total)*100 } catch(ArithmeticException e) { log.info "Could not calculate: Work Done Percentages: "+e } // Success. return result } // getReactiveRatio /** * Selects and returns Immediate Callouts, grouped by Asset. * @param params The request params, may contain params to specify the search. * @param locale The locale to use when generating result.message. */ def getImmediateCallouts(params, locale) { def result = [:] def fail = { Map m -> result.error = [ code: m.code, args: [] ] return result } def namedParams = [:] namedParams.startDate = params.startDate ?: dateUtilService.today namedParams.endDate = params.endDate ?: dateUtilService.today // Auto swap date range. if(namedParams.startDate > namedParams.endDate) { def tempStartDate = namedParams.startDate namedParams.startDate = namedParams.endDate namedParams.endDate = tempStartDate } result.startDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.startDate) result.endDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.endDate) namedParams.endDate++ // Start of next day required. namedParams.immediateCallout = TaskType.read(1) result.taskQuery = "from Task as task \ where (task.trash = false \ and task.targetStartDate < :endDate \ and task.targetStartDate >= :startDate \ and task.taskType = :immediateCallout \ ) \ )" result.taskQuery = "select distinct task " + result.taskQuery result.taskQueryList = Task.executeQuery(result.taskQuery, namedParams) result.taskCount = result.taskQueryList.size() // Assets on Tasks Count. result.totalAssetsOnTasksCount = 0 result.totalDownTime = [total: 0, hours: 0, minutes:0] result.assetList = [] // Task Details result.taskList = [] // Add or update lists. def addToLists = { asset, task -> def downTime = 0 def faultEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(1)) def causeEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(2)) def workDoneEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(3)) def taskDetails = 'Task #'+task.id+' - '+task.description+'\n' faultEntries.each(){ taskDetails += ' Faults: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n' } causeEntries.each(){ taskDetails += ' Causes: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n' } workDoneEntries.each(){ taskDetails += ' Work Done: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n' } faultEntries.each() { downTime += (it.durationHour*60 + it.durationMinute) } result.totalDownTime.total += downTime def assetDetails = result.assetList.find { it.id == asset.id } if(assetDetails) { assetDetails.immediateCalloutCount++ assetDetails.downTime += downTime assetDetails.tasks += taskDetails } else { assetDetails = [id: asset.id, name: asset.name, immediateCalloutCount: 1, downTime: downTime, tasks: taskDetails] result.assetList << assetDetails } } // addAToLists // Summary Of Calculations. result.summaryOfCalculationMethod = 'HQL query: \n\n' def tempStringArray = result.taskQuery.split(' ') tempStringArray.each() { if(it != '') result.summaryOfCalculationMethod += it +'\n' } result.summaryOfCalculationMethod += '\n'+'Calculations: '+'\n\n' result.summaryOfCalculationMethod += 'totalAssetsOnTasksCount = A count of unique assets on each task. \n' result.taskQueryList.each() { task -> if(task.primaryAsset) { result.totalAssetsOnTasksCount++ addToLists(task.primaryAsset, task) } task.associatedAssets.each() { associatedAsset -> if(associatedAsset.id != task.primaryAsset?.id) { result.totalAssetsOnTasksCount++ addToLists(associatedAsset, task) } } } // each() task // Sort assetList by callout count. result.assetList.sort {a, b -> b.immediateCalloutCount.compareTo(a.immediateCalloutCount)} // Calculate hours and minutes. result.totalDownTime.hours = (result.totalDownTime.total / 60).toInteger() result.totalDownTime.minutes = result.totalDownTime.total % 60 // Success. return result } // getImmediateCallouts() /** * Builds and returns a list of RichUiCalendarItem's. * @param params The request params, may contain params to specify the search. * @param locale The locale to use when generating result.message. */ def getWorkLoadSummary(params, locale) { def result = [:] result.displayList = [] def listItem def dayMap = [:] def assignedGroupMap = [:] params.taskInstanceList.each { task -> def dayKey = task.targetStartDate /// @todo: make this key more stable. def assignedTime = 0 task.assignedGroups.each{ assignedGroup -> assignedTime = assignedGroup.estimatedHour*60 + assignedGroup.estimatedMinute } if(dayMap.containsKey(dayKey)) { if(task.description) { dayMap[dayKey] = dayMap[dayKey] + assignedTime } } else { dayMap[(dayKey)] = assignedTime } } dayMap.each { k, v -> listItem = new RichUiCalendarItem(date:k, text:('assignedTime: '+v.toString())) result.displayList << listItem } // Success. return result } // getWorkLoadSummary() } // end class