source: trunk/grails-app/services/TaskRecurringScheduleService.groovy @ 895

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

Domain change, first draft of TaskProcedureRevisions.

File size: 7.2 KB
RevLine 
[199]1class TaskRecurringScheduleService {
2
3    boolean transactional = false
4
[434]5    // Can hold state since the service is a singleton.
6    boolean baseDataWarnLogged = false
7
8    def taskService
[199]9    def dateUtilService
[434]10    def appConfigService
[199]11
[203]12    /**
13    * Generate all enabled recurring tasks.
14    */
[199]15    def generateAll() {
16
[434]17        // Prevent errors if base data has not yet been created.
18        if(!appConfigService.exists("baseDataCreated")) {
19            if(!baseDataWarnLogged) {
20                log.warn "Base data has not been created, can't generate all."
21                baseDataWarnLogged = true
22            }
23            return
24        }
25
[445]26        def p = [:]
27        def targetCompletionDate
28        def nextTargetCompletionDate
29        def tomorrow = dateUtilService.tomorrow
[199]30
[445]31        def taskRecurringScheduleList = TaskRecurringSchedule.findAllByEnabled(true)
32
33        // Stop generation if max reached.
34        def maxSubTasksReached = {
35            if( (it.maxSubTasks > 0) && (it.subTasksGenerated >= it.maxSubTasks) ) {
36                it.enabled = false
37                return true
38            }
39            return false
40        }
41
42        // Stop generation by targetCompletionDate.
43        def targetCompletionDateReached = {
44            if( it.useTargetCompletionDate) {
45                targetCompletionDate = dateUtilService.getMidnight(it.task.targetCompletionDate)
46                nextTargetCompletionDate = dateUtilService.getMidnight(it.nextTargetCompletionDate)
47                if(nextTargetCompletionDate > targetCompletionDate) {
48                    it.enabled = false
49                    return true
50                }
51            }
52            return false
53        }
54
55        // Main loop.
56        for(it in taskRecurringScheduleList) {
57
58            if(maxSubTasksReached(it))
59                continue
60
61            if(targetCompletionDateReached(it))
62                continue
63
[434]64            if (dateUtilService.tomorrow > it.nextGenerationDate) {
[445]65                p = [:]
[243]66
[445]67                // Build the subTask params.
68                p.targetStartDate = it.nextTargetStartDate
69                p.targetCompletionDate = it.nextTargetCompletionDate
[809]70                if(it.task.taskProcedureRevision) p.taskProcedureRevision = it.task.taskProcedureRevision
[445]71                if(it.task.assignedGroups) p.assignedGroups = new ArrayList(it.task.assignedGroups)
72                if(it.task.assignedPersons) p.assignedPersons = new ArrayList(it.task.assignedPersons)
[243]73
[445]74                def result = taskService.createSubTask(it.task, p)
75                if( !result.error ) {
76                    it.lastGeneratedSubTask = result.taskInstance
77                    it.subTasksGenerated++
78                    it.setNextTargetStartDate()
79                    it.setNextGenerationDate()
80                    it.setNextTargetCompletionDate()
81                }
82                else {
83                    log.error "Sub task generation for recurring schedule ${it.id} failed."
84                    log.error result.taskInstance.errors
85                }
[199]86            }
87
[445]88            // Run the completion reached checks for a second time so
89            // that this recurring schedule does not run again if
90            // there are no more subTasks to be generated.
91            if(maxSubTasksReached(it))
92                continue
[199]93
[445]94            if(targetCompletionDateReached(it))
95                continue
96
97        } // for()
98    } // generateAll()
99
[203]100    /**
101    * Creates a new recurring schedule for a task with the given params.
102    * @param params The params to use when creating the new recurring schedule.
103    * @returns A map containing result.error=true (if any error) and result.taskRecurringScheduleInstance and result.taskId.
104    */
105    def create(params) {
106        TaskRecurringSchedule.withTransaction { status ->
107            def result = [:]
108
109            def fail = { Object[] args ->
110                status.setRollbackOnly()
111                if(args.size() == 2) result.taskRecurringScheduleInstance.errors.rejectValue(args[0], args[1])
112                result.error = true
113                return result
114            }
115
116            result.taskRecurringScheduleInstance = new TaskRecurringSchedule(params)
117            result.taskId = result.taskRecurringScheduleInstance.task.id
118
119            if(!result.taskRecurringScheduleInstance.validate())
120                return fail()
121
122            def taskInstance = Task.lock(result.taskId)
123
124            if(!taskInstance)
125                return fail('task', "task.notFound")
126
127            if(taskInstance.taskRecurringSchedule)
128                return fail('task', "tast.taskRecurringSchedule.alreadyExists")
129
130            if(taskInstance.taskStatus.id == 3)
131                return fail('task', "task.operationNotPermittedOnCompleteTask")
132
133            if(taskInstance.trash)
134                return fail('task', "task.operationNotPermittedOnTaskInTrash")
135
[219]136            if(result.taskRecurringScheduleInstance.nextTargetStartDate < dateUtilService.today)
[213]137                return fail("nextTargetStartDate", "taskRecurringSchedule.nextTargetStartDate.mayNotBePast")
[203]138
139            taskInstance.taskRecurringSchedule = result.taskRecurringScheduleInstance
140
141            if(!result.taskRecurringScheduleInstance.save() || !taskInstance.save())
142                return fail()
143
[207]144             // If we get here all went well.
[203]145            return result
146
147        } //end withTransaction
148    } // end create()
149
[207]150    /**
151    * Updates an existing recurring schedule.
152    * @param params The params to update for recurring schedule with id of params.id.
153    * @returns A map containing result.error=true (if any error) and result.taskRecurringScheduleInstance (if available).
154    */
155    def update(params) {
156        TaskRecurringSchedule.withTransaction { status ->
157            def result = [:]
158
159            def fail = { Object[] args ->
160                status.setRollbackOnly()
161                if(args.size() == 2) result.taskRecurringScheduleInstance.errors.rejectValue(args[0], args[1])
162                result.error = true
163                return result
164            }
165
166            result.taskRecurringScheduleInstance = TaskRecurringSchedule.get(params.id)
167
168            if(!result.taskRecurringScheduleInstance)
169                return fail('id', "taskRecurringSchedule.notFound")
170
171            // Optimistic locking check.
172            if(params.version) {
173                def version = params.version.toLong()
174                if(result.taskRecurringScheduleInstance.version > version)
175                    return fail("version", "default.optimistic.locking.failure")
176            }
177
178            result.taskRecurringScheduleInstance.properties = params
179
[219]180            if(result.taskRecurringScheduleInstance.nextTargetStartDate < dateUtilService.today)
[213]181                return fail("nextTargetStartDate", "taskRecurringSchedule.nextTargetStartDate.mayNotBePast")
[207]182
183            result.taskRecurringScheduleInstance.setNextGenerationDate()
184            result.taskRecurringScheduleInstance.setNextTargetCompletionDate()
185
186            if(result.taskRecurringScheduleInstance.hasErrors() || !result.taskRecurringScheduleInstance.save())
187                return fail()
188
189            // If we get here all went well.
190            return result
191
192        } //end withTransaction
193    }  // end update()
194
[199]195} // end of class
Note: See TracBrowser for help on using the repository browser.