source: trunk/grails-app/services/TaskService.groovy @ 414

Last change on this file since 414 was 395, checked in by gav, 14 years ago

Added a create breakin task feature.

File size: 27.1 KB
Line 
1/**
2* Provides a service class for the Task domain class.
3*
4*/
5class TaskService {
6
7    boolean transactional = false
8
9    def authService
10    def assignedGroupService
11    def assignedPersonService
12
13    /**
14    * Determines and returns a possible parent list for a task.
15    * @todo Create and use another method that limits the results to say the latest 20 or 100 tasks?
16    * @param taskInstance The task to use when determining the possible parent list.
17    * @returns A list of the possible parents.
18    */
19    def possibleParentList(taskInstance) {
20        def criteria = taskInstance.createCriteria()
21        def possibleParentList = criteria {
22            and {
23                notEqual('trash', true)
24                notEqual('id', taskInstance.id)
25                taskInstance.subTasks.each() { notEqual('id', it.id) }
26                }
27        }
28    }
29
30    /**
31    * Creates a new task with the given params.
32    * @param params The params to use when creating the new task.
33    * @returns A map containing result.error=true (if any error) and result.taskInstance.
34    */
35    def save(params) {
36        Task.withTransaction { status ->
37            def result = [:]
38            // Default status to "not started" if not supplied.
39            params.taskStatus = params.taskStatus ?: TaskStatus.get(1)
40
41            // Set budgetStatus.
42            if(params.taskType?.id?.toLong() == 1) // Unscheduled Breakin.
43                params.taskBudgetStatus = params.taskBudgetStatus ?: TaskBudgetStatus.get(1) // Unplanned.
44            else
45                params.taskBudgetStatus = params.taskBudgetStatus ?: TaskBudgetStatus.get(2) // Planned.
46
47            def taskInstance = new Task(params)
48            result.taskInstance = taskInstance
49
50            if(result.taskInstance.parentTask?.trash) {
51                status.setRollbackOnly()
52                result.taskInstance.errors.rejectValue("parentTask", "task.operationNotPermittedOnTaskInTrash")
53                result.error = true
54                return result
55            }
56
57            if(taskInstance.save()) {
58                def taskModification = new TaskModification(person: authService.currentUser,
59                                                        taskModificationType: TaskModificationType.get(1),
60                                                        task: taskInstance)
61
62                if(!taskModification.save()) {
63                    status.setRollbackOnly()
64                    taskInstance.errors.rejectValue("taskModifications", "task.modifications.failedToSave")
65                    result.error = true
66                    return result
67                }
68
69                //Add the assignedGroups, provided by a new ArrayList(task.assignedGroups)
70                if(params.assignedGroups) {
71                    def assignedGroupsResult
72                    def assignedGroupParams = [:]
73                    params.assignedGroups.each() {
74
75                        assignedGroupParams = [personGroup: it.personGroup,
76                                                                    task: taskInstance,
77                                                                    estimatedHour: it.estimatedHour,
78                                                                    estimatedMinute: it.estimatedMinute]
79
80                        assignedGroupsResult = assignedGroupService.save(assignedGroupParams)
81
82                        if(assignedGroupsResult.error) {
83                            status.setRollbackOnly()
84                            taskInstance.errors.rejectValue("assignedGroups", "task.assignedGroups.failedToSave")
85                            result.error = true
86                            return result
87                        }
88
89                    }
90                }
91
92                //Add the assignedPersons, provided by a new ArrayList(task.assignedPersons)
93                if(params.assignedPersons) {
94                    def assignedPersonsResult
95                    def assignedPersonsParams = [:]
96                    params.assignedPersons.each() {
97
98                        assignedPersonsParams = [person: it.person,
99                                                                    task: taskInstance,
100                                                                    estimatedHour: it.estimatedHour,
101                                                                    estimatedMinute: it.estimatedMinute]
102
103                        assignedPersonsResult = assignedPersonService.save(assignedPersonsParams)
104
105                        if(assignedPersonsResult.error) {
106                            status.setRollbackOnly()
107                            taskInstance.errors.rejectValue("assignedPersons", "task.assignedPersons.failedToSave")
108                            result.error = true
109                            return result
110                        }
111
112                    }
113                }
114
115                // Success.
116                return result
117            }
118            else {
119                result.error = true
120                return result
121            }
122
123        } //end withTransaction
124    } // end save()
125
126    /**
127    * Creates a subTask copying sane attributes from the parentTask unless otherwise specified in params.
128    * The taskProcedure is only assigned to the sub task if supplied in params.
129    * The assignedPersons and assignedGroups are only added to the sub task if supplied in params.
130    * Collections in params must be supplied as new ArrayList's.
131    * This method is not intended to be a copyTask method.
132    * There should be no reason to copy tasks, try to find a better solution.
133    * @param parentTask The parent task to get attributes from, also set as the parent.
134    * @param params Overrides the parent task values if specified.
135    * @returns A map containing result.error=true (if any error) and result.taskInstance.
136    */
137    def createSubTask(parentTask, params = [:]) {
138
139        def result = [:]
140
141        //Make our new Task a subTask and set the required properites.
142        def p = [:]
143        p.parentTask = parentTask
144        p.description = params.description ?: parentTask.description
145        p.comment = params.comment ?: parentTask.comment
146        p.targetStartDate = params.targetStartDate ?: parentTask.targetStartDate
147        p.targetCompletionDate = params.targetCompletionDate ?: parentTask.targetCompletionDate
148
149        p.taskGroup = params.taskGroup ?: parentTask.taskGroup
150        p.taskStatus = TaskStatus.get(1) // A new subTask must always be "Not Started".
151        p.taskPriority = parentTask.taskPriority
152        p.taskType = params.taskType ?: parentTask.taskType
153        p.leadPerson = params.leadPerson ?: parentTask.leadPerson
154        p.primaryAsset = params.primaryAsset ?: parentTask.primaryAsset
155        p.associatedAssets = params.associatedAssets ?: new ArrayList(parentTask.associatedAssets) // Collection.
156
157        // Only if supplied, otherwise this would be copying.
158        if(params.scheduled) p.scheduled = params.scheduled
159        if(params.approved) p.approved = params.approved
160
161        // Supplied by recurring tasks.
162        if(params.taskProcedure) p.taskProcedure = params.taskProcedure
163        if(params.assignedGroups) p.assignedGroups = params.assignedGroups // Collection.
164        if(params.assignedPersons) p.assignedPersons = params.assignedPersons // Collection.
165
166        // trash: A new subTask must always have trash=false, which is already the domain class default.
167
168        // These would be considered copying, hence not done.
169        // taskRecurringSchedule, entries, taskModifications, subTasks, inventoryMovements.
170
171        // Create the sub task and return the result.
172        result = save(p)
173
174    } // end createSubTask()
175
176    /**
177    * Creates a new task entry.
178    * @param params The params to use when creating the new entry.
179    * @returns A map containing result.error=true (if any error), result.entryInstance and result.taskId.
180    */
181    def saveEntry(params) {
182        Task.withTransaction { status ->
183            def result = [:]
184
185            def fail = { Map m ->
186                status.setRollbackOnly()
187                if(result.taskInstance && m.field)
188                    result.taskInstance.errors.rejectValue(m.field, m.code)
189                result.error = [ code: m.code, args: ["Entry", params.id] ]
190                return result
191            }
192
193            result.entryInstance = new Entry(params)
194            result.entryInstance.enteredBy = authService.currentUser
195
196            def taskInstance
197            if(result.entryInstance.task.id) {
198                result.taskId = result.entryInstance.task.id
199                taskInstance = Task.lock(result.entryInstance.task.id)
200            }
201
202            if(!taskInstance)
203                return fail(field:"task", code:"task.notFound")
204
205            if(result.entryInstance.hasErrors() || !result.entryInstance.save())
206                return fail(code:"default.create.failure")
207
208            if(taskInstance.taskStatus.id == 3)
209                return fail(field:"task", code:"task.operationNotPermittedOnCompleteTask")
210
211            // If task status is "Not Started" and entry type is "Work Done" then we create the started modification and set the status.
212            if(taskInstance.taskStatus.id == 1 && result.entryInstance.entryType.id == 2) {
213
214                // Create the "Started" task modification, this provides the "Actual Started Date".
215                def taskModification = new TaskModification(person: authService.currentUser,
216                                                        taskModificationType: TaskModificationType.get(2),
217                                                        task: taskInstance)
218
219                if(taskModification.hasErrors() || !taskModification.save())
220                    return fail(field:"task", code:"task.modifications.failedToSave")
221
222                // Set task status to "In Progress".
223                taskInstance.taskStatus = TaskStatus.get(2)
224
225                if(taskInstance.hasErrors() || !taskInstance.save())
226                    return fail(field:"task", code:"task.failedToSave")
227            }
228
229            if(result.entryInstance.hasErrors() || !result.entryInstance.save())
230                return fail(field:"task", code:"default.create.failure")
231
232            // Success.
233            return result
234
235        } //end withTransaction
236    } // end saveEntry()
237
238    /**
239    * Updates an existing task.
240    * @param params The params to update for task with id of params.id.
241    * @returns A map containing result.error=true (if any error) and result.taskInstance (if available).
242    */
243    def update(params) {
244        Task.withTransaction { status ->
245            def result = [:]
246
247            def fail = { Object[] args ->
248                status.setRollbackOnly()
249                if(args.size() == 2) result.taskInstance.errors.rejectValue(args[0], args[1])
250                result.error = true
251                return result
252            }
253
254            result.taskInstance = Task.get(params.id)
255
256            if(!result.taskInstance)
257                return fail('id', "task.notFound")
258
259            // Optimistic locking check.
260            if(params.version) {
261                def version = params.version.toLong()
262                if(result.taskInstance.version > version)
263                    return fail("version", "default.optimistic.locking.failure")
264            }
265
266            result.taskInstance.properties = params
267
268            if(result.taskInstance.hasErrors() || !result.taskInstance.save())
269                return fail()
270
271            def taskModification = new TaskModification(person:authService.currentUser,
272                                                    taskModificationType: TaskModificationType.get(3),
273                                                    task: result.taskInstance)
274
275            if(!taskModification.save())
276                return fail("taskModifications", "task.modifications.failedToSave")
277
278            // If we get here all went well.
279            return result
280
281        } //end withTransaction
282    }  // end update()
283
284    /**
285    * Completes an existing task.
286    * @param params The params for task with id of params.id.
287    * @returns A map containing result.error=true (if any error) and result.taskInstance.
288    */
289    def complete(params) {
290        Task.withTransaction { status ->
291            def result = [:]
292            result.taskInstance = Task.get(params.id)
293            if(result.taskInstance) {
294
295                // Optimistic locking check.
296                if(params.version) {
297                    def version = params.version.toLong()
298                    if(result.taskInstance.version > version) {
299                        status.setRollbackOnly()
300                        result.taskInstance.errors.rejectValue("version", "task.optimistic.locking.failure", "Another user has updated this Task while you were editing.")
301                        result.error = true
302                        return result
303                    }
304                }
305
306                result.taskInstance.taskStatus = TaskStatus.get(3)
307                result.taskInstance.taskRecurringSchedule?.enabled = false
308
309                if(result.taskInstance.save()) {
310                    def taskModification = new TaskModification(person:authService.currentUser,
311                                                            taskModificationType: TaskModificationType.get(4),
312                                                            task: result.taskInstance)
313
314                    if(taskModification.save()) {
315                        // All went well.
316                        return result
317                    }
318                    else {
319                        status.setRollbackOnly()
320                        result.taskInstance.errors.rejectValue("taskModifications", "task.modifications.failedToSave")
321                        result.error = true
322                        return result
323                    }
324                }
325            }
326            // Something failed.
327            status.setRollbackOnly()
328            result.error = true
329            return result
330
331        } //end withTransaction
332    }  // end complete()
333
334    /**
335    * Reopens an existing task.
336    * @param params The params for task with id of params.id.
337    * @returns A map containing result.error=true (if any error) and result.taskInstance.
338    */
339    def reopen(params) {
340        Task.withTransaction { status ->
341            def result = [:]
342            result.taskInstance = Task.get(params.id)
343            if(result.taskInstance) {
344
345                // Optimistic locking check.
346                if(params.version) {
347                    def version = params.version.toLong()
348                    if(result.taskInstance.version > version) {
349                        status.setRollbackOnly()
350                        result.taskInstance.errors.rejectValue("version", "task.optimistic.locking.failure", "Another user has updated this Task while you were editing.")
351                        result.error = true
352                        return result
353                    }
354                }
355
356                result.taskInstance.taskStatus = TaskStatus.get(2)
357
358                if(result.taskInstance.save()) {
359                    def taskModification = new TaskModification(person:authService.currentUser,
360                                                            taskModificationType: TaskModificationType.get(5),
361                                                            task: result.taskInstance)
362                    if(taskModification.save()) {
363                        // All went well.
364                        return result
365                    }
366                    else {
367                        status.setRollbackOnly()
368                        result.taskInstance.errors.rejectValue("taskModifications", "task.modifications.failedToSave")
369                        result.error = true
370                        return result
371                    }
372                }
373            }
374            // Something failed.
375            status.setRollbackOnly()
376            result.error = true
377            return result
378
379        } //end withTransaction
380    }  // end reopen()
381
382    /**
383    * Move a task to the trash.
384    * @param params The params for task with id of params.id.
385    * @returns A map containing result.error=true (if any error) and result.taskInstance.
386    */
387    def trash(params) {
388        Task.withTransaction { status ->
389            def result = [:]
390            result.taskInstance = Task.get(params.id)
391            if(result.taskInstance) {
392
393                // Optimistic locking check.
394                if(params.version) {
395                    def version = params.version.toLong()
396                    if(result.taskInstance.version > version) {
397                        status.setRollbackOnly()
398                        result.taskInstance.errors.rejectValue("version", "task.optimistic.locking.failure", "Another user has updated this Task while you were editing.")
399                        result.error = true
400                        return result
401                    }
402                }
403
404                result.taskInstance.trash = true
405                result.taskInstance.taskRecurringSchedule?.enabled = false
406
407                if(result.taskInstance.save()) {
408                    def taskModification = new TaskModification(person:authService.currentUser,
409                                                            taskModificationType: TaskModificationType.get(6),
410                                                            task: result.taskInstance)
411                    if(taskModification.save()) {
412                        // All went well.
413                        return result
414                    }
415                    else {
416                        status.setRollbackOnly()
417                        result.taskInstance.errors.rejectValue("taskModifications", "task.modifications.failedToSave")
418                        result.error = true
419                        return result
420                    }
421                }
422            }
423            // Something failed.
424            status.setRollbackOnly()
425            result.error = true
426            return result
427
428        } //end withTransaction
429    }  // end trash()
430
431    /**
432    * Restore a task from the trash.
433    * @param params The params for task with id of params.id.
434    * @returns A map containing result.error=true (if any error) and result.taskInstance.
435    */
436    def restore(params) {
437        Task.withTransaction { status ->
438            def result = [:]
439            result.taskInstance = Task.get(params.id)
440            if(result.taskInstance) {
441
442                // Optimistic locking check.
443                if(params.version) {
444                    def version = params.version.toLong()
445                    if(result.taskInstance.version > version) {
446                        status.setRollbackOnly()
447                        result.taskInstance.errors.rejectValue("version", "task.optimistic.locking.failure", "Another user has updated this Task while you were editing.")
448                        result.error = true
449                        return result
450                    }
451                }
452
453                result.taskInstance.trash = false
454
455                if(result.taskInstance.save()) {
456                    def taskModification = new TaskModification(person:authService.currentUser,
457                                                            taskModificationType: TaskModificationType.get(7),
458                                                            task: result.taskInstance)
459                    if(taskModification.save()) {
460                        // All went well.
461                        return result
462                    }
463                    else {
464                        status.setRollbackOnly()
465                        result.taskInstance.errors.rejectValue("taskModifications", "task.modifications.failedToSave")
466                        result.error = true
467                        return result
468                    }
469                }
470            }
471            // Something failed.
472            status.setRollbackOnly()
473            result.error = true
474            return result
475
476        } //end withTransaction
477    }  // end restore()
478
479    /**
480    * Approve a task.
481    * @param params The params for task with id of params.id.
482    * @returns A map containing result.error=true (if any error) and result.taskInstance.
483    */
484    def approve(params) {
485        Task.withTransaction { status ->
486            def result = [:]
487            result.taskInstance = Task.get(params.id)
488            if(result.taskInstance) {
489
490                // Optimistic locking check.
491                if(params.version) {
492                    def version = params.version.toLong()
493                    if(result.taskInstance.version > version) {
494                        status.setRollbackOnly()
495                        result.taskInstance.errors.rejectValue("version", "task.optimistic.locking.failure", "Another user has updated this Task while you were editing.")
496                        result.error = true
497                        return result
498                    }
499                }
500
501                result.taskInstance.approved = true
502
503                if(result.taskInstance.save()) {
504                    def taskModification = new TaskModification(person:authService.currentUser,
505                                                            taskModificationType: TaskModificationType.get(8),
506                                                            task: result.taskInstance)
507                    if(taskModification.save()) {
508                        // All went well.
509                        return result
510                    }
511                    else {
512                        status.setRollbackOnly()
513                        result.taskInstance.errors.rejectValue("taskModifications", "task.modifications.failedToSave")
514                        result.error = true
515                        return result
516                    }
517                }
518            }
519            // Something failed.
520            status.setRollbackOnly()
521            result.error = true
522            return result
523
524        } //end withTransaction
525    }  // end approve()
526
527    /**
528    * Remove a previously given approval from a task.
529    * @param params The params for task with id of params.id.
530    * @returns A map containing result.error=true (if any error) and result.taskInstance.
531    */
532    def renegeApproval(params) {
533        Task.withTransaction { status ->
534            def result = [:]
535            result.taskInstance = Task.get(params.id)
536            if(result.taskInstance) {
537
538                // Optimistic locking check.
539                if(params.version) {
540                    def version = params.version.toLong()
541                    if(result.taskInstance.version > version) {
542                        status.setRollbackOnly()
543                        result.taskInstance.errors.rejectValue("version", "task.optimistic.locking.failure", "Another user has updated this Task while you were editing.")
544                        result.error = true
545                        return result
546                    }
547                }
548
549                result.taskInstance.approved = false
550
551                if(result.taskInstance.save()) {
552                    def taskModification = new TaskModification(person:authService.currentUser,
553                                                            taskModificationType: TaskModificationType.get(9),
554                                                            task: result.taskInstance)
555                    if(taskModification.save()) {
556                        // All went well.
557                        return result
558                    }
559                    else {
560                        status.setRollbackOnly()
561                        result.taskInstance.errors.rejectValue("taskModifications", "task.modifications.failedToSave")
562                        result.error = true
563                        return result
564                    }
565                }
566            }
567            // Something failed.
568            status.setRollbackOnly()
569            result.error = true
570            return result
571
572        } //end withTransaction
573    }  // end renegeApproval()
574
575    /**
576    * Creates a new breakin task with the given params.
577    * @param params The params to use when creating the new task.
578    * @returns A map containing result.error=true (if any error) and result.taskInstance.
579    */
580    def saveBreakin(params) {
581        Task.withTransaction { status ->
582            def result = [:]
583
584            def fail = { Map m ->
585                status.setRollbackOnly()
586                if(result.taskInstance && m.field)
587                    result.taskInstance.errors.rejectValue(m.field, m.code)
588                result.error = [ code: m.code, args: ["Task", params.id] ]
589                return result
590            }
591
592            // If not supplied.
593            if(!params.taskStatus)
594                params.taskStatus = TaskStatus.get(1) // Not Started.
595
596            result.taskInstance = new Task(params)
597
598            // Always for a breakin.
599            result.taskInstance.taskType = TaskType.get(1) // Unscheduled Breakin.
600            result.taskInstance.taskBudgetStatus = TaskBudgetStatus.get(1) // Unplanned.
601            result.taskInstance.taskPriority = TaskPriority.get(4) // Immediate.
602            result.taskInstance.taskGroup = TaskGroup.get(1) // Engineering Activites.
603            result.taskInstance.approved = true
604            result.taskInstance.leadPerson = authService.currentUser
605
606            if(result.taskInstance.hasErrors() || !result.taskInstance.save())
607                fail(code:"default.create.failure")
608
609            if(!result.error) {
610                def taskModification = new TaskModification(person: authService.currentUser,
611                                                                taskModificationType: TaskModificationType.get(1), // Created.
612                                                                task: result.taskInstance)
613
614                if(taskModification.hasErrors() || !taskModification.save())
615                    fail(field:"taskModifications", code:"task.modifications.failedToSave")
616            }
617
618            def faultParams = [task: result.taskInstance,
619                                            entryType: EntryType.get(1),
620                                            comment: params.entryFault.comment,
621                                            durationHour: params.entryFault.durationHour,
622                                            durationMinute: params.entryFault.durationMinute]
623            def faultResult = saveEntry(faultParams)
624            result.entryFaultInstance = faultResult.entryInstance
625
626            def workDoneParams = [task: result.taskInstance,
627                                                    entryType: EntryType.get(2),
628                                                    comment: params.entryWorkDone.comment,
629                                                    durationHour: params.entryWorkDone.durationHour,
630                                                    durationMinute: params.entryWorkDone.durationMinute]
631            def workDoneResult = saveEntry(workDoneParams)
632            result.entryWorkDoneInstance = workDoneResult.entryInstance
633
634            if(result.error)
635                return result
636
637            if(faultResult.error)
638                return fail(code: "default.create.failure")
639
640            if(workDoneResult.error)
641                return fail(code: "default.create.failure")
642
643            // Success.
644            return result
645
646        } //end withTransaction
647    } // end saveBreakin()
648
649} // end TaskService
Note: See TracBrowser for help on using the repository browser.