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

Last change on this file since 708 was 708, checked in by gav, 13 years ago

Fix small bug in reactive ratio report, where BigDecimal? not returned and therefore setScale() not available.
Also return error message for endDate < startDate.

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