class TaskRecurringScheduleService { boolean transactional = false // Can hold state since the service is a singleton. boolean baseDataWarnLogged = false def taskService def dateUtilService def appConfigService /** * Generate all enabled recurring tasks. */ def generateAll() { // Prevent errors if base data has not yet been created. if(!appConfigService.exists("baseDataCreated")) { if(!baseDataWarnLogged) { log.warn "Base data has not been created, can't generate all." baseDataWarnLogged = true } return } def p = [:] def targetCompletionDate def nextTargetCompletionDate def tomorrow = dateUtilService.tomorrow def taskRecurringScheduleList = TaskRecurringSchedule.findAllByEnabled(true) // Stop generation if max reached. def maxSubTasksReached = { if( (it.maxSubTasks > 0) && (it.subTasksGenerated >= it.maxSubTasks) ) { it.enabled = false return true } return false } // Stop generation by targetCompletionDate. def targetCompletionDateReached = { if( it.useTargetCompletionDate) { targetCompletionDate = dateUtilService.getMidnight(it.task.targetCompletionDate) nextTargetCompletionDate = dateUtilService.getMidnight(it.nextTargetCompletionDate) if(nextTargetCompletionDate > targetCompletionDate) { it.enabled = false return true } } return false } // Main loop. for(it in taskRecurringScheduleList) { if(maxSubTasksReached(it)) continue if(targetCompletionDateReached(it)) continue if (dateUtilService.tomorrow > it.nextGenerationDate) { p = [:] // Build the subTask params. p.targetStartDate = it.nextTargetStartDate p.targetCompletionDate = it.nextTargetCompletionDate if(it.task.taskProcedureRevision) p.taskProcedureRevision = it.task.taskProcedureRevision if(it.task.assignedGroups) p.assignedGroups = new ArrayList(it.task.assignedGroups) if(it.task.assignedPersons) p.assignedPersons = new ArrayList(it.task.assignedPersons) def result = taskService.createSubTask(it.task, p) if( !result.error ) { it.lastGeneratedSubTask = result.taskInstance it.subTasksGenerated++ it.setNextTargetStartDate() it.setNextGenerationDate() it.setNextTargetCompletionDate() } else { log.error "Sub task generation for recurring schedule ${it.id} failed." log.error result.taskInstance.errors } } // Run the completion reached checks for a second time so // that this recurring schedule does not run again if // there are no more subTasks to be generated. if(maxSubTasksReached(it)) continue if(targetCompletionDateReached(it)) continue } // for() } // generateAll() /** * Creates a new recurring schedule for a task with the given params. * @param params The params to use when creating the new recurring schedule. * @returns A map containing result.error=true (if any error) and result.taskRecurringScheduleInstance and result.taskId. */ def create(params) { TaskRecurringSchedule.withTransaction { status -> def result = [:] def fail = { Object[] args -> status.setRollbackOnly() if(args.size() == 2) result.taskRecurringScheduleInstance.errors.rejectValue(args[0], args[1]) result.error = true return result } result.taskRecurringScheduleInstance = new TaskRecurringSchedule(params) result.taskId = result.taskRecurringScheduleInstance.task.id if(!result.taskRecurringScheduleInstance.validate()) return fail() def taskInstance = Task.lock(result.taskId) if(!taskInstance) return fail('task', "task.notFound") if(taskInstance.taskRecurringSchedule) return fail('task', "tast.taskRecurringSchedule.alreadyExists") if(taskInstance.taskStatus.id == 3) return fail('task', "task.operationNotPermittedOnCompleteTask") if(taskInstance.trash) return fail('task', "task.operationNotPermittedOnTaskInTrash") if(result.taskRecurringScheduleInstance.nextTargetStartDate < dateUtilService.today) return fail("nextTargetStartDate", "taskRecurringSchedule.nextTargetStartDate.mayNotBePast") taskInstance.taskRecurringSchedule = result.taskRecurringScheduleInstance if(!result.taskRecurringScheduleInstance.save() || !taskInstance.save()) return fail() // If we get here all went well. return result } //end withTransaction } // end create() /** * Updates an existing recurring schedule. * @param params The params to update for recurring schedule with id of params.id. * @returns A map containing result.error=true (if any error) and result.taskRecurringScheduleInstance (if available). */ def update(params) { TaskRecurringSchedule.withTransaction { status -> def result = [:] def fail = { Object[] args -> status.setRollbackOnly() if(args.size() == 2) result.taskRecurringScheduleInstance.errors.rejectValue(args[0], args[1]) result.error = true return result } result.taskRecurringScheduleInstance = TaskRecurringSchedule.get(params.id) if(!result.taskRecurringScheduleInstance) return fail('id', "taskRecurringSchedule.notFound") // Optimistic locking check. if(params.version) { def version = params.version.toLong() if(result.taskRecurringScheduleInstance.version > version) return fail("version", "default.optimistic.locking.failure") } result.taskRecurringScheduleInstance.properties = params if(result.taskRecurringScheduleInstance.nextTargetStartDate < dateUtilService.today) return fail("nextTargetStartDate", "taskRecurringSchedule.nextTargetStartDate.mayNotBePast") result.taskRecurringScheduleInstance.setNextGenerationDate() result.taskRecurringScheduleInstance.setNextTargetCompletionDate() if(result.taskRecurringScheduleInstance.hasErrors() || !result.taskRecurringScheduleInstance.save()) return fail() // If we get here all went well. return result } //end withTransaction } // end update() } // end of class