import org.codehaus.groovy.runtime.TimeCategory class TaskRecurringSchedule { Task lastGeneratedSubTask Period recurPeriod Period generateAheadPeriod Period taskDurationPeriod Integer recurEvery = 1 Integer taskDuration = 0 Integer generateAhead = 1 Date startDate = new Date() Date lastGeneratedDate Date nextTargetStartDate = new Date() Date nextTargetCompletionDate = new Date() Date nextGenerationDate = new Date() boolean isEnabled = true // static hasMany = [] static belongsTo = [task: Task] static constraints = { recurEvery(min:1, max:365) taskDuration(min:0, max:365) generateAhead(min:0, max:365) lastGeneratedDate(nullable:true) lastGeneratedSubTask(nullable:true) } String toString() { "Recur every ${recurEvery} ${recurPeriod}" } // As of Grails 1.1.1 this does not fire/pass before validation. // But setting some defaults above to pass validation and placing this code here // in the hope that this will be fixed in future versions. def beforeInsert = { nextTargetStartDate = startDate setNextGenerationDate() setNextTargetCompletionDate() } public void setNextGenerationDate() { switch (generateAheadPeriod.period) { case "Day(s)": use(TimeCategory) { nextGenerationDate = nextTargetStartDate - generateAhead.days } break case "Week(s)": use(TimeCategory) { nextGenerationDate = nextTargetStartDate - generateAhead.weeks } break case "Month(s)": use(TimeCategory) { nextGenerationDate = nextTargetStartDate - generateAhead.months } break case "Year(s)": use(TimeCategory) { nextGenerationDate = nextTargetStartDate - generateAhead.years } break default: break } def now = new Date() if( nextGenerationDate < now) {nextGenerationDate = now} } public void setNextTargetCompletionDate() { switch (taskDurationPeriod.period) { case "Day(s)": use(TimeCategory) { nextTargetCompletionDate = nextTargetStartDate + taskDuration.days } break case "Week(s)": use(TimeCategory) { nextTargetCompletionDate = nextTargetStartDate + taskDuration.weeks } break case "Month(s)": use(TimeCategory) { nextTargetCompletionDate = nextTargetStartDate + taskDuration.months } break case "Year(s)": use(TimeCategory) { nextTargetCompletionDate = nextTargetStartDate + taskDuration.years } break default: break } } }