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

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

Added a create breakin task feature.

File size: 27.1 KB
RevLine 
[202]1/**
2* Provides a service class for the Task domain class.
[196]3*
4*/
[137]5class TaskService {
6
[180]7    boolean transactional = false
[137]8
[291]9    def authService
[251]10    def assignedGroupService
11    def assignedPersonService
[137]12
[202]13    /**
[203]14    * Determines and returns a possible parent list for a task.
[245]15    * @todo Create and use another method that limits the results to say the latest 20 or 100 tasks?
[202]16    * @param taskInstance The task to use when determining the possible parent list.
[196]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
[202]30    /**
[196]31    * Creates a new task with the given params.
[202]32    * @param params The params to use when creating the new task.
[196]33    * @returns A map containing result.error=true (if any error) and result.taskInstance.
34    */
[394]35    def save(params) {
[180]36        Task.withTransaction { status ->
37            def result = [:]
[181]38            // Default status to "not started" if not supplied.
39            params.taskStatus = params.taskStatus ?: TaskStatus.get(1)
[252]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
[180]47            def taskInstance = new Task(params)
48            result.taskInstance = taskInstance
49
[196]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
[180]57            if(taskInstance.save()) {
[291]58                def taskModification = new TaskModification(person: authService.currentUser,
[180]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
[243]69                //Add the assignedGroups, provided by a new ArrayList(task.assignedGroups)
70                if(params.assignedGroups) {
[251]71                    def assignedGroupsResult
72                    def assignedGroupParams = [:]
[243]73                    params.assignedGroups.each() {
74
[251]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) {
[243]83                            status.setRollbackOnly()
84                            taskInstance.errors.rejectValue("assignedGroups", "task.assignedGroups.failedToSave")
85                            result.error = true
86                            return result
87                        }
[251]88
[243]89                    }
90                }
91
92                //Add the assignedPersons, provided by a new ArrayList(task.assignedPersons)
93                if(params.assignedPersons) {
[251]94                    def assignedPersonsResult
95                    def assignedPersonsParams = [:]
[243]96                    params.assignedPersons.each() {
97
[251]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) {
[243]106                            status.setRollbackOnly()
107                            taskInstance.errors.rejectValue("assignedPersons", "task.assignedPersons.failedToSave")
108                            result.error = true
109                            return result
110                        }
[251]111
[243]112                    }
113                }
114
115                // Success.
[180]116                return result
117            }
118            else {
119                result.error = true
120                return result
121            }
122
123        } //end withTransaction
[394]124    } // end save()
[180]125
[202]126    /**
[245]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.
[196]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
[245]146        p.targetStartDate = params.targetStartDate ?: parentTask.targetStartDate
147        p.targetCompletionDate = params.targetCompletionDate ?: parentTask.targetCompletionDate
[196]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
[245]155        p.associatedAssets = params.associatedAssets ?: new ArrayList(parentTask.associatedAssets) // Collection.
[196]156
[245]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
[196]160
[245]161        // Supplied by recurring tasks.
[202]162        if(params.taskProcedure) p.taskProcedure = params.taskProcedure
[245]163        if(params.assignedGroups) p.assignedGroups = params.assignedGroups // Collection.
164        if(params.assignedPersons) p.assignedPersons = params.assignedPersons // Collection.
[202]165
[245]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.
[394]172        result = save(p)
[196]173
174    } // end createSubTask()
175
[202]176    /**
[196]177    * Creates a new task entry.
[202]178    * @param params The params to use when creating the new entry.
[196]179    * @returns A map containing result.error=true (if any error), result.entryInstance and result.taskId.
180    */
[394]181    def saveEntry(params) {
[186]182        Task.withTransaction { status ->
183            def result = [:]
[395]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
[186]193            result.entryInstance = new Entry(params)
[291]194            result.entryInstance.enteredBy = authService.currentUser
[180]195
[395]196            def taskInstance
197            if(result.entryInstance.task.id) {
[186]198                result.taskId = result.entryInstance.task.id
[395]199                taskInstance = Task.lock(result.entryInstance.task.id)
200            }
[186]201
[395]202            if(!taskInstance)
203                return fail(field:"task", code:"task.notFound")
[186]204
[395]205            if(result.entryInstance.hasErrors() || !result.entryInstance.save())
206                return fail(code:"default.create.failure")
[186]207
[395]208            if(taskInstance.taskStatus.id == 3)
209                return fail(field:"task", code:"task.operationNotPermittedOnCompleteTask")
[186]210
[395]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) {
[186]213
[395]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)
[186]218
[395]219                if(taskModification.hasErrors() || !taskModification.save())
220                    return fail(field:"task", code:"task.modifications.failedToSave")
[186]221
[395]222                // Set task status to "In Progress".
223                taskInstance.taskStatus = TaskStatus.get(2)
[186]224
[395]225                if(taskInstance.hasErrors() || !taskInstance.save())
226                    return fail(field:"task", code:"task.failedToSave")
[186]227            }
228
[395]229            if(result.entryInstance.hasErrors() || !result.entryInstance.save())
230                return fail(field:"task", code:"default.create.failure")
231
232            // Success.
233            return result
234
[186]235        } //end withTransaction
[394]236    } // end saveEntry()
[186]237
[202]238    /**
239    * Updates an existing task.
240    * @param params The params to update for task with id of params.id.
[204]241    * @returns A map containing result.error=true (if any error) and result.taskInstance (if available).
[202]242    */
[180]243    def update(params) {
244        Task.withTransaction { status ->
245            def result = [:]
[204]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
[180]254            result.taskInstance = Task.get(params.id)
255
[204]256            if(!result.taskInstance)
[206]257                return fail('id', "task.notFound")
[180]258
[204]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            }
[180]265
[204]266            result.taskInstance.properties = params
267
268            if(result.taskInstance.hasErrors() || !result.taskInstance.save())
269                return fail()
270
[291]271            def taskModification = new TaskModification(person:authService.currentUser,
[204]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.
[180]279            return result
280
281        } //end withTransaction
282    }  // end update()
283
[202]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    */
[181]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)
[202]307                result.taskInstance.taskRecurringSchedule?.enabled = false
[181]308
309                if(result.taskInstance.save()) {
[291]310                    def taskModification = new TaskModification(person:authService.currentUser,
[181]311                                                            taskModificationType: TaskModificationType.get(4),
312                                                            task: result.taskInstance)
[201]313
[181]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
[180]332    }  // end complete()
333
[202]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    */
[181]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()) {
[291]359                    def taskModification = new TaskModification(person:authService.currentUser,
[181]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
[180]380    }  // end reopen()
381
[202]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    */
[181]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
[202]405                result.taskInstance.taskRecurringSchedule?.enabled = false
[181]406
407                if(result.taskInstance.save()) {
[291]408                    def taskModification = new TaskModification(person:authService.currentUser,
[181]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
[180]429    }  // end trash()
430
[202]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    */
[181]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()) {
[291]456                    def taskModification = new TaskModification(person:authService.currentUser,
[181]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
[180]477    }  // end restore()
478
[202]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    */
[181]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()) {
[291]504                    def taskModification = new TaskModification(person:authService.currentUser,
[181]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
[180]525    }  // end approve()
526
[202]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    */
[181]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()) {
[291]552                    def taskModification = new TaskModification(person:authService.currentUser,
[181]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
[180]573    }  // end renegeApproval()
574
[395]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
[180]649} // end TaskService
Note: See TracBrowser for help on using the repository browser.