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

Last change on this file since 807 was 445, checked in by gav, 15 years ago

Add maxSubTasks and useTargetCompletionDate to TaskRecurringSchedule as features to end subTask generation.

File size: 7.1 KB
Line 
1class TaskRecurringScheduleService {
2
3    boolean transactional = false
4
5    // Can hold state since the service is a singleton.
6    boolean baseDataWarnLogged = false
7
8    def taskService
9    def dateUtilService
10    def appConfigService
11
12    /**
13    * Generate all enabled recurring tasks.
14    */
15    def generateAll() {
16
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
26        def p = [:]
27        def targetCompletionDate
28        def nextTargetCompletionDate
29        def tomorrow = dateUtilService.tomorrow
30
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
64            if (dateUtilService.tomorrow > it.nextGenerationDate) {
65                p = [:]
66
67                // Build the subTask params.
68                p.targetStartDate = it.nextTargetStartDate
69                p.targetCompletionDate = it.nextTargetCompletionDate
70                if(it.task.taskProcedure) p.taskProcedure = it.task.taskProcedure
71                if(it.task.assignedGroups) p.assignedGroups = new ArrayList(it.task.assignedGroups)
72                if(it.task.assignedPersons) p.assignedPersons = new ArrayList(it.task.assignedPersons)
73
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                }
86            }
87
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
93
94            if(targetCompletionDateReached(it))
95                continue
96
97        } // for()
98    } // generateAll()
99
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
136            if(result.taskRecurringScheduleInstance.nextTargetStartDate < dateUtilService.today)
137                return fail("nextTargetStartDate", "taskRecurringSchedule.nextTargetStartDate.mayNotBePast")
138
139            taskInstance.taskRecurringSchedule = result.taskRecurringScheduleInstance
140
141            if(!result.taskRecurringScheduleInstance.save() || !taskInstance.save())
142                return fail()
143
144             // If we get here all went well.
145            return result
146
147        } //end withTransaction
148    } // end create()
149
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
180            if(result.taskRecurringScheduleInstance.nextTargetStartDate < dateUtilService.today)
181                return fail("nextTargetStartDate", "taskRecurringSchedule.nextTargetStartDate.mayNotBePast")
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
195} // end of class
Note: See TracBrowser for help on using the repository browser.