source: trunk/grails-app/services/TaskReportService.groovy @ 954

Last change on this file since 954 was 732, checked in by gav, 14 years ago

Auto swap date range in all reports.

File size: 15.0 KB
RevLine 
[585]1import org.gnumims.RichUiCalendarItem
[533]2
3/**
[651]4* Service class that encapsulates the business logic for Task Reports.
[533]5*/
6class TaskReportService {
7
8    boolean transactional = false
9
10    def authService
11    def dateUtilService
12//     def messageSource
13
[544]14    def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
[533]15
16    def paramsMax = 100000
17
18    /**
19    * Selects and returns the reactive ratio.
[542]20    * @param params The request params, may contain params to specify the search.
[533]21    * @param locale The locale to use when generating result.message.
22    */
23    def getReactiveRatio(params, locale) {
24        def result = [:]
25
[708]26        def fail = { Map m ->
27            result.error = [ code: m.code, args: [] ]
28            return result
29        }
30
[533]31        def namedParams = [:]
32        namedParams.startDate = params.startDate ?: dateUtilService.today
[535]33        namedParams.endDate = params.endDate ?: dateUtilService.today
[708]34
[732]35        // Auto swap date range.
36        if(namedParams.startDate > namedParams.endDate) {
37            def tempStartDate = namedParams.startDate
38            namedParams.startDate = namedParams.endDate
39            namedParams.endDate = tempStartDate
40        }
[708]41
[732]42        result.startDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.startDate)
43        result.endDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.endDate)
44
[535]45        namedParams.endDate++ // Start of next day required.
[708]46
[533]47        namedParams.immediateCallout = TaskType.read(1)
48        namedParams.unscheduledBreakin = TaskType.read(2)
49        namedParams.preventativeMaintenance = TaskType.read(4)
[535]50        namedParams.notStarted = TaskStatus.read(1)
[533]51
52        result.taskQuery = "from Task as task \
[542]53                                            where (task.trash = false \
54                                                        and task.taskStatus != :notStarted \
55                                                        and task.targetStartDate < :endDate \
56                                                        and task.targetStartDate >= :startDate \
57                                                        and ( \
58                                                            task.taskType = :immediateCallout \
59                                                            or task.taskType = :unscheduledBreakin \
60                                                            or task.taskType = :preventativeMaintenance \
61                                                        ) \
62                                            )"
[533]63
[535]64        result.taskQuery = "select distinct task " + result.taskQuery
65        result.taskList = Task.executeQuery(result.taskQuery, namedParams)
[533]66        result.taskCount = result.taskList.size()
67
[536]68        // Assets on Tasks Count.
[535]69        result.totalAssetsOnTasksCount = 0
[533]70        result.immediateCalloutCount = 0
71        result.unscheduledBreakinCount = 0
72        result.preventativeMaintenanceCount = 0
73
[542]74        // Summary Of Calculations.
[535]75        result.summaryOfCalculationMethod = 'HQL query: \n\n'
76        def tempStringArray = result.taskQuery.split('    ')
77        tempStringArray.each() {
78            if(it != '') result.summaryOfCalculationMethod += it +'\n'
79        }
80        result.summaryOfCalculationMethod += '\n'+'Calculations: '+'\n\n'
81
82        result.summaryOfCalculationMethod += 'totalAssetsOnTasksCount = A count of unique assets on each task. \n'
[533]83        result.taskList.each() { task ->
84            if(task.primaryAsset) {
[535]85                result.totalAssetsOnTasksCount++
[533]86                if(task.taskType == namedParams.immediateCallout) result.immediateCalloutCount++
87                if(task.taskType == namedParams.unscheduledBreakin) result.unscheduledBreakinCount++
88                if(task.taskType == namedParams.preventativeMaintenance) result.preventativeMaintenanceCount++
89            }
90            task.associatedAssets.each() { associatedAsset ->
91                if(associatedAsset.id != task.primaryAsset?.id) {
[535]92                    result.totalAssetsOnTasksCount++
[533]93                    if(task.taskType == namedParams.immediateCallout) result.immediateCalloutCount++
94                    if(task.taskType == namedParams.unscheduledBreakin) result.unscheduledBreakinCount++
95                    if(task.taskType == namedParams.preventativeMaintenance) result.preventativeMaintenanceCount++
96                }
97            }
98        } // each() task
99
[536]100        // Percentage of counts.
[533]101        result.immediateCalloutPercentage = 0
102        result.totalPreventativePercentage = 0
103
[535]104        result.summaryOfCalculationMethod += 'totalPreventativeCount = unscheduledBreakinCount + preventativeMaintenanceCount\n'
[533]105        result.totalPreventativeCount = result.unscheduledBreakinCount + result.preventativeMaintenanceCount
106        try {
[535]107            result.summaryOfCalculationMethod += 'immediateCalloutPercentage = (immediateCalloutCount / totalAssetsOnTasksCount)*100 \n'
108            result.summaryOfCalculationMethod += 'totalPreventativePercentage = (totalPreventativeCount / totalAssetsOnTasksCount)*100 \n'
109            result.immediateCalloutPercentage = (result.immediateCalloutCount / result.totalAssetsOnTasksCount)*100
110            result.totalPreventativePercentage = (result.totalPreventativeCount / result.totalAssetsOnTasksCount)*100
[533]111        }
112        catch(ArithmeticException e) {
[708]113            log.info "Could not calculate: Assets on Tasks Percentages: "+e
[533]114        }
115
[536]116        // Work Done.
[708]117        result.immediateCalloutWorkDone = [total:0, hours:0, minutes:0, percentage: new BigDecimal(0)]
[536]118        result.unscheduledBreakinWorkDone = [total:0, hours:0, minutes:0]
119        result.preventativeMaintenanceWorkDone = [total:0, hours:0, minutes:0]
[708]120        result.totalPreventativeWorkDone = [total:0, hours:0, minutes:0, percentage: new BigDecimal(0)]
[536]121        result.totalWorkDone = [total:0, hours:0, minutes:0]
122
123        result.taskList.each() { task ->
124            task.entries.each() { entry ->
125                // Has assets assigned and is Work Done.
126                if( (task.primaryAsset || task.associatedAssets) && entry.entryType.id == 3L ) {
127                        if(task.taskType == namedParams.immediateCallout)
128                            result.immediateCalloutWorkDone.total += (entry.durationHour*60) + entry.durationMinute
129                        if(task.taskType == namedParams.unscheduledBreakin)
130                            result.unscheduledBreakinWorkDone.total += (entry.durationHour*60) + entry.durationMinute
131                        if(task.taskType == namedParams.preventativeMaintenance)
132                            result.preventativeMaintenanceWorkDone.total += (entry.durationHour*60) + entry.durationMinute
133                }
134            } // each() entry
135        } // each() task
136
137        // Work Done hours and minutes.
138        result.immediateCalloutWorkDone.hours = (result.immediateCalloutWorkDone.total / 60).toInteger()
139        result.immediateCalloutWorkDone.minutes = result.immediateCalloutWorkDone.total % 60
140
141        result.unscheduledBreakinWorkDone.hours = (result.unscheduledBreakinWorkDone.total / 60).toInteger()
142        result.unscheduledBreakinWorkDone.minutes = result.unscheduledBreakinWorkDone.total % 60
143
144        result.preventativeMaintenanceWorkDone.hours = (result.preventativeMaintenanceWorkDone.total / 60).toInteger()
145        result.preventativeMaintenanceWorkDone.minutes = result.preventativeMaintenanceWorkDone.total % 60
146
147        // Work Done Totals.
148        result.totalPreventativeWorkDone.total = result.unscheduledBreakinWorkDone.total + result.preventativeMaintenanceWorkDone.total
149        result.totalPreventativeWorkDone.hours = (result.totalPreventativeWorkDone.total / 60).toInteger()
150        result.totalPreventativeWorkDone.minutes = result.totalPreventativeWorkDone.total % 60
151
152        result.totalWorkDone.total = result.immediateCalloutWorkDone.total + result.totalPreventativeWorkDone.total
153        result.totalWorkDone.hours = (result.totalWorkDone.total / 60).toInteger()
154        result.totalWorkDone.minutes = result.totalWorkDone.total % 60
155
156        // Work Done Percentages.
157        try {
[708]158            result.immediateCalloutWorkDone.percentage = (BigDecimal)(result.immediateCalloutWorkDone.total / result.totalWorkDone.total)*100
159            result.totalPreventativeWorkDone.percentage = (BigDecimal)(result.totalPreventativeWorkDone.total / result.totalWorkDone.total)*100
[536]160        }
161        catch(ArithmeticException e) {
[708]162            log.info "Could not calculate: Work Done Percentages: "+e
[536]163        }
164
[533]165        // Success.
166        return result
167
[542]168    } // getReactiveRatio
[533]169
[542]170    /**
171    * Selects and returns Immediate Callouts, grouped by Asset.
172    * @param params The request params, may contain params to specify the search.
173    * @param locale The locale to use when generating result.message.
174    */
175    def getImmediateCallouts(params, locale) {
176        def result = [:]
[533]177
[709]178        def fail = { Map m ->
179            result.error = [ code: m.code, args: [] ]
180            return result
181        }
182
[542]183        def namedParams = [:]
184        namedParams.startDate = params.startDate ?: dateUtilService.today
[709]185        namedParams.endDate = params.endDate ?: dateUtilService.today
186
[732]187        // Auto swap date range.
188        if(namedParams.startDate > namedParams.endDate) {
189            def tempStartDate = namedParams.startDate
190            namedParams.startDate = namedParams.endDate
191            namedParams.endDate = tempStartDate
192        }
[709]193
[732]194        result.startDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.startDate)
195        result.endDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.endDate)
196
[709]197        namedParams.endDate++ // Start of next day required.
198
[542]199        namedParams.immediateCallout = TaskType.read(1)
200
201        result.taskQuery = "from Task as task \
202                                            where (task.trash = false \
[709]203                                                        and task.targetStartDate < :endDate \
[542]204                                                        and task.targetStartDate >= :startDate \
205                                                        and task.taskType = :immediateCallout \
206                                                        ) \
207                                            )"
208
209        result.taskQuery = "select distinct task " + result.taskQuery
[544]210        result.taskQueryList = Task.executeQuery(result.taskQuery, namedParams)
211        result.taskCount = result.taskQueryList.size()
[542]212
213        // Assets on Tasks Count.
214        result.totalAssetsOnTasksCount = 0
[544]215        result.totalDownTime = [total: 0, hours: 0, minutes:0]
[542]216        result.assetList = []
217
[544]218        // Task Details
219        result.taskList = []
[542]220
[544]221        // Add or update lists.
222        def addToLists = { asset, task ->
223
[542]224            def downTime = 0
225            def faultEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(1))
[544]226            def causeEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(2))
227            def workDoneEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(3))
228            def taskDetails = 'Task #'+task.id+' - '+task.description+'\n'
229            faultEntries.each(){
230                taskDetails += '    Faults: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n'
231            }
232            causeEntries.each(){
233                taskDetails += '    Causes: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n'
234            }
235            workDoneEntries.each(){
236                taskDetails += '    Work Done: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n'
237            }
238
[542]239            faultEntries.each() { downTime += (it.durationHour*60 + it.durationMinute) }
[544]240            result.totalDownTime.total += downTime
[542]241
242            def assetDetails = result.assetList.find { it.id == asset.id }
243            if(assetDetails) {
244                assetDetails.immediateCalloutCount++
[544]245                assetDetails.downTime += downTime
246                assetDetails.tasks += taskDetails
[542]247            }
248            else {
249                assetDetails = [id: asset.id,
250                                            name: asset.name,
251                                            immediateCalloutCount: 1,
252                                            downTime: downTime,
[544]253                                            tasks: taskDetails]
[542]254
255                result.assetList << assetDetails
256            }
[544]257        } // addAToLists
[542]258
259        // Summary Of Calculations.
260        result.summaryOfCalculationMethod = 'HQL query: \n\n'
261        def tempStringArray = result.taskQuery.split('    ')
262        tempStringArray.each() {
263            if(it != '') result.summaryOfCalculationMethod += it +'\n'
264        }
265        result.summaryOfCalculationMethod += '\n'+'Calculations: '+'\n\n'
266
267        result.summaryOfCalculationMethod += 'totalAssetsOnTasksCount = A count of unique assets on each task. \n'
[544]268        result.taskQueryList.each() { task ->
[542]269            if(task.primaryAsset) {
270                result.totalAssetsOnTasksCount++
[544]271                addToLists(task.primaryAsset, task)
[542]272            }
273            task.associatedAssets.each() { associatedAsset ->
274                if(associatedAsset.id != task.primaryAsset?.id) {
275                    result.totalAssetsOnTasksCount++
[544]276                    addToLists(associatedAsset, task)
[542]277                }
278            }
279
280        } // each() task
281
[544]282        // Sort assetList by callout count.
[542]283        result.assetList.sort {a, b -> b.immediateCalloutCount.compareTo(a.immediateCalloutCount)}
284
[544]285        // Calculate hours and minutes.
286        result.totalDownTime.hours = (result.totalDownTime.total / 60).toInteger()
287        result.totalDownTime.minutes = result.totalDownTime.total % 60
288
[542]289        // Success.
290        return result
291
292    } // getImmediateCallouts()
293
[585]294    /**
295    * Builds and returns a list of RichUiCalendarItem's.
296    * @param params The request params, may contain params to specify the search.
297    * @param locale The locale to use when generating result.message.
298    */
299    def getWorkLoadSummary(params, locale) {
[542]300
[585]301        def result = [:]
302        result.displayList = []
303
304        def listItem
305
306        def dayMap = [:]
307        def assignedGroupMap = [:]
308
309        params.taskInstanceList.each { task ->
310            def dayKey = task.targetStartDate /// @todo: make this key more stable.
311            def assignedTime = 0
312
313            task.assignedGroups.each{ assignedGroup ->
314                assignedTime = assignedGroup.estimatedHour*60 + assignedGroup.estimatedMinute
315            }
316
317            if(dayMap.containsKey(dayKey)) {
318                if(task.description) {
319                    dayMap[dayKey] = dayMap[dayKey] + assignedTime
320                }
321            }
322            else {
323                dayMap[(dayKey)] = assignedTime
324            }
325        }
326
327        dayMap.each { k, v ->
328                listItem = new RichUiCalendarItem(date:k, text:('assignedTime: '+v.toString()))
329                result.displayList << listItem
330        }
331
332        // Success.
333        return result
334
335    } // getWorkLoadSummary()
336
[533]337} // end class
Note: See TracBrowser for help on using the repository browser.