import net.kromhouts.HqlBuilder import grails.orm.PagedResultList import org.hibernate.FetchMode as FM /** * Service class that encapsulates the business logic for Task searches. */ class TaskSearchService { boolean transactional = false def authService def dateUtilService def messageSource def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib() def paramsMax = 100000 // Be sure to update taskQuickSearchPane.js if quickSearchSelection is changed. def getQuickSearchSelection() { [ 'allTasks': g.message(code: 'task.search.text.all.tasks'), 'personsImmediateCallouts': g.message(code: 'task.search.text.persons.immediate.callouts'), 'personsTasks': g.message(code: 'task.search.text.persons.tasks') ] } /** * Selects and returns the correct search results based on the supplied quickSearch. * @param params The request params, may contain params.quickSearch string to specify the search. * @param locale The locale to use when generating result.message. */ def getQuickSearch(params = [:], locale) { def result = [:] result.quickSearch = params.quickSearch ?: "personsTasks" if(params.person) result.person = Person.get(params.person.id.toLong()) else result.person = authService.currentUser result.startDate = params.startDate ?: dateUtilService.today result.endDate = params.endDate ?: dateUtilService.today // Auto swap date range. if(result.startDate > result.endDate) { def tempStartDate = result.startDate result.startDate = result.endDate result.endDate = tempStartDate } result.includeCompleted = params.includeCompleted = params.includeCompleted ? true:false def getMessage = { Map m -> messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale) } def formatted = { Date d -> g.formatDate(format: "EEE, dd-MMM-yyyy", date: d) } def startOfToday = dateUtilService.today def startOfYesterday = dateUtilService.yesterday def startOfTomorrow = dateUtilService.tomorrow def oneWeekAgo = dateUtilService.oneWeekAgo def formattedStartOfToday = formatted(startOfToday) def formattedStartOfYesterday = formatted(startOfYesterday) def formattedStartOfTomorrow = formatted(startOfTomorrow) def formattedOneWeekAgo = formatted(oneWeekAgo) def allTasks = { result.taskInstanceList = getTasks(params, result.startDate, result.endDate+1) if(result.taskInstanceList.totalCount > 0) { if(result.startDate == result.endDate) result.message = getMessage(code:"task.search.text.all.tasks.message", args:[ formatted(result.startDate) ]) else result.message = getMessage(code:"task.search.text.all.tasks.between.message", args:[ formatted(result.startDate), formatted(result.endDate) ]) } else { if(result.startDate == result.endDate) result.message = getMessage(code:"task.search.text.all.tasks.none.found", args:[ formatted(result.startDate) ]) else result.message = getMessage(code:"task.search.text.all.tasks.between.none.found", args:[ formatted(result.startDate), formatted(result.endDate) ]) } } def personsTasks = { result.taskInstanceList = getPersonsTasks(params, result.person, result.startDate, result.endDate+1) if(result.taskInstanceList.totalCount > 0) { if(result.startDate == result.endDate) result.message = getMessage(code:"task.search.text.persons.tasks.message", args:[ result.person, formatted(result.startDate) ]) else result.message = getMessage(code:"task.search.text.persons.tasks.between.message", args:[ result.person, formatted(result.startDate), formatted(result.endDate) ]) } else { if(result.startDate == result.endDate) result.message = getMessage(code:"task.search.text.persons.tasks.none.found", args:[ result.person, formatted(result.startDate) ]) else result.message = getMessage(code:"task.search.text.persons.tasks.between.none.found", args:[ result.person, formatted(result.startDate), formatted(result.endDate) ]) } } def personsImmediateCallouts = { result.taskInstanceList = getPersonsImmediateCallouts(params, result.person, result.startDate, result.endDate+1) if(result.taskInstanceList.totalCount > 0) { if(result.startDate == result.endDate) result.message = getMessage(code:"task.search.text.persons.immediate.callouts.message", args:[ result.person, formatted(result.startDate) ]) else result.message = getMessage(code:"task.search.text.persons.immediate.callouts.between.message", args:[ result.person, formatted(result.startDate), formatted(result.endDate) ]) } else { if(result.startDate == result.endDate) result.message = getMessage(code:"task.search.text.persons.immediate.callouts.none.found", args:[ result.person, formatted(result.startDate) ]) else result.message = getMessage(code:"task.search.text.persons.immediate.callouts.between.none.found", args:[ result.person, formatted(result.startDate), formatted(result.endDate) ]) } } switch (result.quickSearch) { case "myTodays": result.quickSearch = "personsTasks" result.startDate = startOfToday result.endDate = startOfToday personsTasks() break case "myYesterdays": result.quickSearch = "personsTasks" result.startDate = startOfYesterday result.endDate = startOfYesterday personsTasks() break case "myTomorrows": result.quickSearch = "personsTasks" result.startDate = startOfTomorrow result.endDate = startOfTomorrow personsTasks() break case "myPastWeek": result.quickSearch = "personsTasks" result.startDate = oneWeekAgo result.endDate = startOfToday personsTasks() break case "personsTasks": personsTasks() break case "personsImmediateCallouts": personsImmediateCallouts() break case "todays": result.quickSearch = "allTasks" result.startDate = startOfToday result.endDate = startOfToday allTasks() break case "yesterdays": result.quickSearch = "allTasks" result.startDate = startOfYesterday result.endDate = startOfToday allTasks() break case "tomorrows": result.quickSearch = "allTasks" result.startDate = startOfTomorrow result.endDate = startOfTomorrow+1 allTasks() break case "pastWeek": result.quickSearch = "allTasks" result.startDate = oneWeekAgo result.endDate = startOfTomorrow allTasks() break case "allTasks": allTasks() break default: //case "plannersRange": result.quickSearch = "allTasks" result.startDate = oneWeekAgo result.endDate = startOfToday+15 allTasks() break } // switch. // Success. return result } // getQuickSearch /** * Get all tasks that are not in the trash bin, by default today's tasks. * @param params The request params. * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to). * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than). */ def getTasks(params, startDate=null, endDate=null) { def paginateParams = [:] paginateParams.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax) paginateParams.offset = params?.offset?.toInteger() ?: 0 def orderBy = '' if(params.sort?.contains('.')) // protect against filterpane bug. params.sort = null if(params.sort && params.order) { def sort = "task." + params.sort def order = (params.order == "asc") ? "asc" : "desc" orderBy = " order by " + sort + ' ' + order } else orderBy = " order by task.taskStatus, task.taskPriority, task.targetStartDate" def namedParams = [:] namedParams.startDate = startDate ?: dateUtilService.today namedParams.endDate = endDate ?: dateUtilService.tomorrow def baseQuery = "from Task as task \ where (task.trash = false \ and task.targetStartDate < :endDate \ and task.targetCompletionDate >= :startDate \ )" def searchQuery = "select distinct task " + baseQuery + orderBy def list = Task.executeQuery(searchQuery, namedParams, paginateParams) def countQuery = "select count(distinct task) as taskCount " + baseQuery def totalCount = Task.executeQuery(countQuery, namedParams)[0].toInteger() def taskInstanceList = new PagedResultList(list, totalCount) return taskInstanceList } // getTasks() /** * Get a person's tasks, by default current user and today's tasks. * "My tasks and approved tasks that I am assigned to" * @param params The request params. * @param person The person to get tasks for, defaults to current user. * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to). * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than). */ def getPersonsTasks(params, person=null, startDate=null, endDate=null) { def max = Math.min(params?.max?.toInteger() ?: 10, paramsMax) def offset = params?.offset?.toInteger() ?: 0 def orderBy = '' if(params.sort?.contains('.')) // protect against filterpane bug. params.sort = null if(params.sort && params.order) { def sort = "task." + params.sort def order = (params.order == "asc") ? "asc" : "desc" orderBy = "by " + sort + ' ' + order } else orderBy = "by task.taskPriority, task.targetStartDate, task.taskStatus" def q = new HqlBuilder().query { select 'count(distinct task) as taskCount' from 'Task as task', 'left join task.assignedPersons as assignedPersonOfTask', 'left join assignedPersonOfTask.person as assignedPerson', 'left join task.assignedGroups as assignedGroupOfTask', 'left join assignedGroupOfTask.personGroup as personGroup', 'left join personGroup.persons as assignedPersonViaGroup', 'left join task.taskModifications as taskModification', 'left join taskModification.person as createdBy', 'left join taskModification.taskModificationType as taskModificationType' where 'task.trash = false' and 'task.targetStartDate < :endDate' and 'task.targetCompletionDate >= :startDate' if(!params.includeCompleted) { and 'task.taskStatus.id != 3' // Complete. } and { where '(taskModificationType.id = 1 and createdBy = :person and task.leadPerson = :person)' // Created. or '(task.approved = true and (task.leadPerson = :person or assignedPerson = :person or assignedPersonViaGroup = :person))' } } q.namedParams.person = person ?: authService.currentUser q.namedParams.startDate = startDate ?: dateUtilService.today q.namedParams.endDate = endDate ?: dateUtilService.tomorrow def totalCount = Task.executeQuery(q.query, q.namedParams)[0].toInteger() q.select = "distinct task" q.order = orderBy q.paginateParams.max = max q.paginateParams.offset = offset def list = Task.executeQuery(q.query, q.namedParams, q.paginateParams) def taskInstanceList = new PagedResultList(list, totalCount) return taskInstanceList } // getPersonsTasks() /** * Get a person's immediateCallout tasks, by default all users and today's tasks. * @param params The request params. * @param person The person to get tasks for, defaults to null and therefore all immediateCallouts. * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to). * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than). */ def getPersonsImmediateCallouts(params, person=null, startDate=null, endDate=null) { def max = Math.min(params?.max?.toInteger() ?: 10, paramsMax) def offset = params?.offset?.toInteger() ?: 0 def orderBy = '' if(params.sort?.contains('.')) // protect against filterpane bug. params.sort = null if(params.sort && params.order) { def sort = "task." + params.sort def order = (params.order == "asc") ? "asc" : "desc" orderBy = "by " + sort + ' ' + order } else orderBy = "by task.taskStatus, task.targetStartDate" def q = new HqlBuilder().query { select 'count(distinct task) as taskCount' from 'Task as task', 'left join task.taskModifications as taskModification', 'left join taskModification.person as createdBy', 'left join taskModification.taskModificationType as taskModificationType' where 'task.taskType.id = 1' // Immediate Callout. and 'task.targetStartDate < :endDate' and 'task.targetCompletionDate >= :startDate' if(!params.includeCompleted) { and 'task.taskStatus.id != 3' // Complete. } if(person) { namedParams.person = person and '( (taskModificationType.id = 1 and createdBy = :person) or task.leadPerson = :person)' // Created or Lead Person. } and 'task.trash = false' } q.namedParams.startDate = startDate ?: dateUtilService.today q.namedParams.endDate = endDate ?: dateUtilService.tomorrow def totalCount = Task.executeQuery(q.query, q.namedParams)[0].toInteger() q.select = "distinct task" q.order = orderBy q.paginateParams.max = max q.paginateParams.offset = offset def list = Task.executeQuery(q.query, q.namedParams, q.paginateParams) def taskInstanceList = new PagedResultList(list, totalCount) return taskInstanceList } // getPersonsImmediateCallouts() /** * Get work done by person and date. * A person ID and date may be specified in params otherwise the current user and today are used. * @param params The request params. * @returns A map containing entries, totalEntries, startOfDay, person, totalHours, totalMinutes. */ def getWorkDone(params, locale) { def result = [:] result.person = params.person?.id ? Person.get(params.person.id.toInteger()) : authService.currentUser if(params.date_year && params.date_month && params.date_day) result.startOfDay = dateUtilService.makeDate(params.date_year, params.date_month, params.date_day) else result.startOfDay = dateUtilService.today result.startOfNextDay = result.startOfDay + 1 def formattedStartOfDay = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.startOfDay) def getMessage = { Map m -> messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale) } result.entries = Entry.createCriteria().list() { eq("enteredBy", result.person) ge("dateDone", result.startOfDay) lt("dateDone", result.startOfNextDay) entryType { eq("id", 3L) } } // createCriteria result.totalEntries = result.entries.size() if(result.totalEntries > 0) result.message = getMessage(code:"task.search.text.work.done.message", args:[result.person, formattedStartOfDay]) else result.message = getMessage(code:"task.search.text.work.done.none.found", args:[result.person, formattedStartOfDay]) result.totalHours = 0 result.totalMinutes = 0 result.entries.each() { result.totalMinutes += (it.durationHour*60) + it.durationMinute } result.totalHours = (result.totalMinutes / 60).toInteger() result.totalMinutes = result.totalMinutes % 60 return result } // getWorkDone() /** * Get work load by task group and date. * Group ID's and date range may be specified in params otherwise no group and today are used. * @param params The request params. * @returns A map containing the results. */ def getWorkLoad(params, locale) { def result = [:] def max = 1000 // TaskStatus.. result.taskStatusList = [] if(params.taskStatusList instanceof String) result.taskStatusList << TaskStatus.get(params.taskStatusList.toInteger()) else if(params.taskStatusList) result.taskStatusList = TaskStatus.getAll( params.taskStatusList.collect {it.toInteger()} ) // TaskGroups. result.taskGroups = [] if(params.taskGroups instanceof String) result.taskGroups << TaskGroup.get(params.taskGroups.toInteger()) else if(params.taskGroups) result.taskGroups = TaskGroup.getAll( params.taskGroups.collect {it.toInteger()} ) // Start Date. if(params.startDate_year && params.startDate_month && params.startDate_day) result.startDate = dateUtilService.makeDate(params.startDate_year, params.startDate_month, params.startDate_day) else result.startDate = dateUtilService.today // End Date. if(params.endDate_year && params.endDate_month && params.endDate_day) result.endDate = dateUtilService.makeDate(params.endDate_year, params.endDate_month, params.endDate_day) else result.endDate = result.startDate // Auto swap date range. if(result.startDate > result.endDate) { def tempStartDate = result.startDate result.startDate = result.endDate result.endDate = tempStartDate } def formattedStartDate = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.startDate) def formattedEndDate = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.endDate) def getMessage = { Map m -> messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale) } result.tasks = new PagedResultList([], 0) if(result.taskGroups && result.taskStatusList) { result.tasks = Task.createCriteria().list(max: max) { eq("trash", false) lt("targetStartDate", result.endDate+1) ge("targetCompletionDate", result.startDate) inList("taskStatus", result.taskStatusList) inList("taskGroup", result.taskGroups) order("taskStatus", "asc") order("taskPriority", "asc") order("targetStartDate", "asc") fetchMode("assignedGroups", FM.EAGER) fetchMode("assignedGroups.personGroup", FM.EAGER) } // createCriteria } result.tasks.list.unique() result.totalHours = 0 result.totalMinutes = 0 result.workLoadGroups = [:] // Exit early! if(result.tasks.totalCount > result.tasks.size()) { result.errorMessage = getMessage(code:"task.search.text.work.load.too.many.results", args:[result.tasks.size(), result.tasks.totalCount]) return result } else if(result.tasks.size() > 0) result.message = getMessage(code:"task.search.text.work.load.message", args:[formattedStartDate, formattedEndDate]) else result.message = getMessage(code:"task.search.text.work.load.none.found", args:[formattedStartDate, formattedEndDate]) // Collect all assignedGroups. def assignedGroups = [] for(task in result.tasks) { for(assignedGroup in task.assignedGroups) { assignedGroups << assignedGroup } } // Calculate work load for each personGroup and minute totals. def tempHours = 0 def tempMinutes = 0 def personGroup for(assignedGroup in assignedGroups) { personGroup = assignedGroup.personGroup if(!result.workLoadGroups.containsKey(personGroup)) { result.workLoadGroups[personGroup] = [hours: 0, minutes: 0] } tempMinutes = (assignedGroup.estimatedHour*60) + assignedGroup.estimatedMinute result.totalMinutes += tempMinutes result.workLoadGroups[personGroup].minutes += tempMinutes } // Resolve totals and sort. result.workLoadGroups.each { workLoadGroup -> workLoadGroup.value.hours = (workLoadGroup.value.minutes / 60).toInteger() workLoadGroup.value.minutes = workLoadGroup.value.minutes % 60 } result.workLoadGroups = result.workLoadGroups.sort { p1, p2 -> p1.key.name.compareToIgnoreCase(p2.key.name) } result.totalHours = (result.totalMinutes / 60).toInteger() result.totalMinutes = result.totalMinutes % 60 // Success. return result } // getWorkLoad() } // end class