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

Last change on this file since 719 was 709, checked in by gav, 14 years ago

Return error message on immediate callouts report submit when endDate < startDate.

File size: 14.3 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 fail = { Map m ->
172            result.error = [ code: m.code, args: [] ]
173            return result
174        }
175
176        def namedParams = [:]
177        namedParams.startDate = params.startDate ?: dateUtilService.today
178        namedParams.endDate = params.endDate ?: dateUtilService.today
179
180        if(namedParams.endDate < namedParams.startDate)
181            return fail(code: "default.end.date.before.start.date")
182
183        namedParams.endDate++ // Start of next day required.
184
185        namedParams.immediateCallout = TaskType.read(1)
186
187        result.taskQuery = "from Task as task \
188                                            where (task.trash = false \
189                                                        and task.targetStartDate < :endDate \
190                                                        and task.targetStartDate >= :startDate \
191                                                        and task.taskType = :immediateCallout \
192                                                        ) \
193                                            )"
194
195        result.taskQuery = "select distinct task " + result.taskQuery
196        result.taskQueryList = Task.executeQuery(result.taskQuery, namedParams)
197        result.taskCount = result.taskQueryList.size()
198
199        // Assets on Tasks Count.
200        result.totalAssetsOnTasksCount = 0
201        result.totalDownTime = [total: 0, hours: 0, minutes:0]
202        result.assetList = []
203
204        // Task Details
205        result.taskList = []
206
207        // Add or update lists.
208        def addToLists = { asset, task ->
209
210            def downTime = 0
211            def faultEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(1))
212            def causeEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(2))
213            def workDoneEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(3))
214            def taskDetails = 'Task #'+task.id+' - '+task.description+'\n'
215            faultEntries.each(){
216                taskDetails += '    Faults: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n'
217            }
218            causeEntries.each(){
219                taskDetails += '    Causes: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n'
220            }
221            workDoneEntries.each(){
222                taskDetails += '    Work Done: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n'
223            }
224
225            faultEntries.each() { downTime += (it.durationHour*60 + it.durationMinute) }
226            result.totalDownTime.total += downTime
227
228            def assetDetails = result.assetList.find { it.id == asset.id }
229            if(assetDetails) {
230                assetDetails.immediateCalloutCount++
231                assetDetails.downTime += downTime
232                assetDetails.tasks += taskDetails
233            }
234            else {
235                assetDetails = [id: asset.id,
236                                            name: asset.name,
237                                            immediateCalloutCount: 1,
238                                            downTime: downTime,
239                                            tasks: taskDetails]
240
241                result.assetList << assetDetails
242            }
243        } // addAToLists
244
245        // Summary Of Calculations.
246        result.summaryOfCalculationMethod = 'HQL query: \n\n'
247        def tempStringArray = result.taskQuery.split('    ')
248        tempStringArray.each() {
249            if(it != '') result.summaryOfCalculationMethod += it +'\n'
250        }
251        result.summaryOfCalculationMethod += '\n'+'Calculations: '+'\n\n'
252
253        result.summaryOfCalculationMethod += 'totalAssetsOnTasksCount = A count of unique assets on each task. \n'
254        result.taskQueryList.each() { task ->
255            if(task.primaryAsset) {
256                result.totalAssetsOnTasksCount++
257                addToLists(task.primaryAsset, task)
258            }
259            task.associatedAssets.each() { associatedAsset ->
260                if(associatedAsset.id != task.primaryAsset?.id) {
261                    result.totalAssetsOnTasksCount++
262                    addToLists(associatedAsset, task)
263                }
264            }
265
266        } // each() task
267
268        // Sort assetList by callout count.
269        result.assetList.sort {a, b -> b.immediateCalloutCount.compareTo(a.immediateCalloutCount)}
270
271        // Calculate hours and minutes.
272        result.totalDownTime.hours = (result.totalDownTime.total / 60).toInteger()
273        result.totalDownTime.minutes = result.totalDownTime.total % 60
274
275        // Success.
276        return result
277
278    } // getImmediateCallouts()
279
280    /**
281    * Builds and returns a list of RichUiCalendarItem's.
282    * @param params The request params, may contain params to specify the search.
283    * @param locale The locale to use when generating result.message.
284    */
285    def getWorkLoadSummary(params, locale) {
286
287        def result = [:]
288        result.displayList = []
289
290        def listItem
291
292        def dayMap = [:]
293        def assignedGroupMap = [:]
294
295        params.taskInstanceList.each { task ->
296            def dayKey = task.targetStartDate /// @todo: make this key more stable.
297            def assignedTime = 0
298
299            task.assignedGroups.each{ assignedGroup ->
300                assignedTime = assignedGroup.estimatedHour*60 + assignedGroup.estimatedMinute
301            }
302
303            if(dayMap.containsKey(dayKey)) {
304                if(task.description) {
305                    dayMap[dayKey] = dayMap[dayKey] + assignedTime
306                }
307            }
308            else {
309                dayMap[(dayKey)] = assignedTime
310            }
311        }
312
313        dayMap.each { k, v ->
314                listItem = new RichUiCalendarItem(date:k, text:('assignedTime: '+v.toString()))
315                result.displayList << listItem
316        }
317
318        // Success.
319        return result
320
321    } // getWorkLoadSummary()
322
323} // end class
Note: See TracBrowser for help on using the repository browser.