| [479] | 1 | import grails.orm.PagedResultList | 
|---|
 | 2 |  | 
|---|
| [476] | 3 | /** | 
|---|
 | 4 | * Service class that encapsulates the business logic for Task searches. | 
|---|
 | 5 | */ | 
|---|
| [143] | 6 | class TaskSearchService { | 
|---|
 | 7 |  | 
|---|
 | 8 |     boolean transactional = false | 
|---|
 | 9 |  | 
|---|
| [476] | 10 |     def authService | 
|---|
| [143] | 11 |     def dateUtilService | 
|---|
| [476] | 12 |     def messageSource | 
|---|
| [143] | 13 |  | 
|---|
| [490] | 14 |     def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib() | 
|---|
 | 15 |  | 
|---|
| [476] | 16 |     def paramsMax = 100000 | 
|---|
| [260] | 17 |  | 
|---|
| [476] | 18 |     /** | 
|---|
 | 19 |     * Selects and returns the correct search results based on the supplied quickSearch. | 
|---|
 | 20 |     * @param params The request params, may contain params.quickSearch string to specify the search. | 
|---|
 | 21 |     * @param locale The locale to use when generating result.message. | 
|---|
 | 22 |     */ | 
|---|
 | 23 |     def getQuickSearch(params, locale) { | 
|---|
 | 24 |         def result = [:] | 
|---|
| [503] | 25 |         result.quickSearch = params.quickSearch ?: "plannersRange" | 
|---|
 | 26 |  | 
|---|
| [476] | 27 |         def currentUser = authService.currentUser | 
|---|
| [503] | 28 |         def startOfToday = dateUtilService.today | 
|---|
 | 29 |         def startOfYesterday = dateUtilService.yesterday | 
|---|
 | 30 |         def startOfTomorrow = dateUtilService.tomorrow | 
|---|
 | 31 |         def oneWeekAgo = dateUtilService.oneWeekAgo | 
|---|
| [476] | 32 |  | 
|---|
| [503] | 33 |         def formattedStartOfToday = g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfToday) | 
|---|
 | 34 |         def formattedStartOfYesterday = g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfYesterday) | 
|---|
 | 35 |         def formattedStartOfTomorrow = g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfTomorrow) | 
|---|
 | 36 |         def formattedOneWeekAgo = g.formatDate(format: "EEE, dd-MMM-yyyy", date: oneWeekAgo) | 
|---|
 | 37 |  | 
|---|
| [476] | 38 |         def getMessage = { Map m -> | 
|---|
 | 39 |             messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale) | 
|---|
 | 40 |         } | 
|---|
 | 41 |  | 
|---|
 | 42 |         switch (result.quickSearch) { | 
|---|
 | 43 |             case "myTodays": | 
|---|
| [503] | 44 |                 result.taskInstanceList = getPersonsTasks(params) | 
|---|
| [476] | 45 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 46 |                     result.message = getMessage(code:"task.search.text.persons.tasks.message", | 
|---|
 | 47 |                                                                     args:[currentUser, formattedStartOfToday]) | 
|---|
| [476] | 48 |                 else | 
|---|
| [503] | 49 |                     result.message = getMessage(code:"task.search.text.persons.tasks.none.found", | 
|---|
 | 50 |                                                                     args:[currentUser, formattedStartOfToday]) | 
|---|
| [476] | 51 |                 break | 
|---|
 | 52 |             case "myYesterdays": | 
|---|
| [503] | 53 |                 result.taskInstanceList = getPersonsTasks(params, currentUser, startOfYesterday, startOfToday) | 
|---|
| [476] | 54 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 55 |                     result.message = getMessage(code:"task.search.text.persons.tasks.message", | 
|---|
 | 56 |                                                                     args:[currentUser, formattedStartOfYesterday]) | 
|---|
| [476] | 57 |                 else | 
|---|
| [503] | 58 |                     result.message = getMessage(code:"task.search.text.persons.tasks.none.found", | 
|---|
 | 59 |                                                                     args:[currentUser, formattedStartOfYesterday]) | 
|---|
| [476] | 60 |                 break | 
|---|
 | 61 |             case "myTomorrows": | 
|---|
| [503] | 62 |                 result.taskInstanceList = getPersonsTasks(params, currentUser, startOfTomorrow, startOfTomorrow+1) | 
|---|
| [476] | 63 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 64 |                     result.message = getMessage(code:"task.search.text.persons.tasks.message", | 
|---|
 | 65 |                                                                     args:[currentUser, formattedStartOfTomorrow]) | 
|---|
| [476] | 66 |                 else | 
|---|
| [503] | 67 |                     result.message = getMessage(code:"task.search.text.persons.tasks.none.found", | 
|---|
 | 68 |                                                                     args:[currentUser, formattedStartOfTomorrow]) | 
|---|
| [476] | 69 |                 break | 
|---|
 | 70 |             case "myPastWeek": | 
|---|
| [503] | 71 |                 result.taskInstanceList = getPersonsTasks(params, currentUser, oneWeekAgo, startOfTomorrow) | 
|---|
| [476] | 72 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 73 |                     result.message = getMessage(code:"task.search.text.persons.tasks.between.message", | 
|---|
 | 74 |                                                                     args:[currentUser, formattedOneWeekAgo, formattedStartOfToday]) | 
|---|
| [476] | 75 |                 else | 
|---|
| [503] | 76 |                     result.message = getMessage(code:"task.search.text.persons.tasks.between.none.found", | 
|---|
 | 77 |                                                                     args:[currentUser, formattedOneWeekAgo, formattedStartOfToday]) | 
|---|
| [476] | 78 |                 break | 
|---|
 | 79 |             case "todays": | 
|---|
| [503] | 80 |                 result.taskInstanceList = getTasks(params) | 
|---|
| [476] | 81 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 82 |                     result.message = getMessage(code:"task.search.text.all.tasks.message", | 
|---|
 | 83 |                                                                     args:[formattedStartOfToday]) | 
|---|
| [476] | 84 |                 else | 
|---|
| [503] | 85 |                     result.message = getMessage(code:"task.search.text.all.tasks.none.found", | 
|---|
 | 86 |                                                                     args:[formattedStartOfToday]) | 
|---|
| [476] | 87 |                 break | 
|---|
 | 88 |             case "yesterdays": | 
|---|
| [503] | 89 |                 result.taskInstanceList = getTasks(params, startOfYesterday, startOfToday) | 
|---|
| [476] | 90 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 91 |                     result.message = getMessage(code:"task.search.text.all.tasks.message", | 
|---|
 | 92 |                                                                     args:[formattedStartOfYesterday]) | 
|---|
| [476] | 93 |                 else | 
|---|
| [503] | 94 |                     result.message = getMessage(code:"task.search.text.all.tasks.none.found", | 
|---|
 | 95 |                                                                     args:[formattedStartOfYesterday]) | 
|---|
| [476] | 96 |                 break | 
|---|
 | 97 |             case "tomorrows": | 
|---|
| [503] | 98 |                 result.taskInstanceList = getTasks(params, startOfTomorrow, startOfTomorrow+1) | 
|---|
| [476] | 99 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 100 |                     result.message = getMessage(code:"task.search.text.all.tasks.message", | 
|---|
 | 101 |                                                                     args:[formattedStartOfTomorrow]) | 
|---|
| [476] | 102 |                 else | 
|---|
| [503] | 103 |                     result.message = getMessage(code:"task.search.text.all.tasks.none.found", | 
|---|
 | 104 |                                                                     args:[formattedStartOfTomorrow]) | 
|---|
| [476] | 105 |                 break | 
|---|
 | 106 |             case "pastWeek": | 
|---|
| [503] | 107 |                 result.taskInstanceList = getTasks(params, oneWeekAgo, startOfTomorrow) | 
|---|
| [476] | 108 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 109 |                     result.message = getMessage(code:"task.search.text.all.tasks.between.message", | 
|---|
 | 110 |                                                                     args:[formattedOneWeekAgo, formattedStartOfToday]) | 
|---|
| [476] | 111 |                 else | 
|---|
| [503] | 112 |                     result.message = getMessage(code:"task.search.text.all.tasks.between.none.found", | 
|---|
 | 113 |                                                                     args:[formattedOneWeekAgo, formattedStartOfToday]) | 
|---|
| [476] | 114 |                 break | 
|---|
 | 115 |             case "budgetUnplanned": | 
|---|
| [503] | 116 |                 result.taskInstanceList = getBudgetTasks(params, TaskBudgetStatus.read(1), oneWeekAgo, startOfTomorrow) | 
|---|
| [476] | 117 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 118 |                     result.message = getMessage(code:"task.search.text.budget.unplanned.message", | 
|---|
 | 119 |                                                                     args:[formattedOneWeekAgo, formattedStartOfToday]) | 
|---|
| [476] | 120 |                 else | 
|---|
| [503] | 121 |                     result.message = getMessage(code:"task.search.text.budget.unplanned.none.found", | 
|---|
 | 122 |                                                                     args:[formattedOneWeekAgo, formattedStartOfToday]) | 
|---|
| [476] | 123 |                 break | 
|---|
 | 124 |             case "budgetPlanned": | 
|---|
| [503] | 125 |                 result.taskInstanceList = getBudgetTasks(params, TaskBudgetStatus.read(2), oneWeekAgo, startOfTomorrow) | 
|---|
| [476] | 126 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 127 |                     result.message = getMessage(code:"task.search.text.budget.planned.message", | 
|---|
 | 128 |                                                                     args:[formattedOneWeekAgo, formattedStartOfToday]) | 
|---|
| [476] | 129 |                 else | 
|---|
| [503] | 130 |                     result.message = getMessage(code:"task.search.text.budget.planned.none.found", | 
|---|
 | 131 |                                                                     args:[formattedOneWeekAgo, formattedStartOfToday]) | 
|---|
| [476] | 132 |                 break | 
|---|
 | 133 |             default: | 
|---|
| [503] | 134 |                 //case "plannersRange": | 
|---|
 | 135 |                 result.taskInstanceList = getTasks(params, oneWeekAgo, startOfToday+15) | 
|---|
| [476] | 136 |                 if(result.taskInstanceList.totalCount > 0) | 
|---|
| [503] | 137 |                     result.message = getMessage(code:"task.search.text.all.tasks.between.message", | 
|---|
 | 138 |                                                                     args:[formattedOneWeekAgo, | 
|---|
 | 139 |                                                                             g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfToday+14)]) | 
|---|
| [476] | 140 |                 else | 
|---|
| [503] | 141 |                     result.message = getMessage(code:"task.search.text.all.tasks.between.none.found", | 
|---|
 | 142 |                                                                     args:[formattedOneWeekAgo, | 
|---|
 | 143 |                                                                             g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfToday+14)]) | 
|---|
| [476] | 144 |                 break | 
|---|
 | 145 |         } // switch. | 
|---|
 | 146 |  | 
|---|
 | 147 |         // Success. | 
|---|
 | 148 |         return result | 
|---|
 | 149 |  | 
|---|
 | 150 |     } // getQuickSearch | 
|---|
 | 151 |  | 
|---|
| [479] | 152 |     /** | 
|---|
| [503] | 153 |     * Get all tasks that are not in the trash bin, by default today's tasks. | 
|---|
| [479] | 154 |     * @param params The request params. | 
|---|
| [503] | 155 |     * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to). | 
|---|
 | 156 |     * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than). | 
|---|
| [479] | 157 |     */ | 
|---|
| [503] | 158 |     def getTasks(params, startDate=null, endDate=null) { | 
|---|
| [512] | 159 |         def paginateParams = [:] | 
|---|
 | 160 |         paginateParams.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax) | 
|---|
 | 161 |         paginateParams.offset = params?.offset?.toInteger() ?: 0 | 
|---|
| [143] | 162 |  | 
|---|
| [512] | 163 |         def sort = "task." + (params?.sort ?: "attentionFlag") | 
|---|
 | 164 |         def order = params?.order == "asc" ? "asc" : "desc" | 
|---|
 | 165 |         def orderBy = " order by " + sort + ' ' + order | 
|---|
| [143] | 166 |  | 
|---|
| [512] | 167 |         def namedParams = [:] | 
|---|
 | 168 |         namedParams.startDate = startDate ?: dateUtilService.today | 
|---|
 | 169 |         namedParams.endDate = endDate ?: dateUtilService.tomorrow | 
|---|
| [144] | 170 |  | 
|---|
| [512] | 171 |         def baseQuery = "from Task as task \ | 
|---|
 | 172 |                                         where (task.trash = false \ | 
|---|
 | 173 |                                                     and task.targetStartDate < :endDate \ | 
|---|
 | 174 |                                                     and task.targetCompletionDate >= :startDate \ | 
|---|
 | 175 |                                         )" | 
|---|
 | 176 |  | 
|---|
 | 177 |         def searchQuery = "select distinct task " + baseQuery + orderBy | 
|---|
 | 178 |         def list = Task.executeQuery(searchQuery, namedParams, paginateParams) | 
|---|
 | 179 |  | 
|---|
 | 180 |         def countQuery = "select count(distinct task) as taskCount " + baseQuery | 
|---|
 | 181 |         def totalCount = Task.executeQuery(countQuery, namedParams)[0].toInteger() | 
|---|
 | 182 |  | 
|---|
 | 183 |         def taskInstanceList = new PagedResultList(list, totalCount) | 
|---|
 | 184 |         return taskInstanceList | 
|---|
 | 185 |     } // getPTasks() | 
|---|
 | 186 |  | 
|---|
| [479] | 187 |     /** | 
|---|
| [503] | 188 |     * Get a person's tasks, by default current user and today's tasks. | 
|---|
 | 189 |     * "My tasks and approved tasks that I am assigned to" | 
|---|
| [479] | 190 |     * @param params The request params. | 
|---|
| [503] | 191 |     * @param person The person to get tasks for, defaults to current user. | 
|---|
 | 192 |     * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to). | 
|---|
 | 193 |     * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than). | 
|---|
| [479] | 194 |     */ | 
|---|
| [503] | 195 |     def getPersonsTasks(params, person=null, startDate=null, endDate=null) { | 
|---|
| [479] | 196 |         def paginateParams = [:] | 
|---|
 | 197 |         paginateParams.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax) | 
|---|
 | 198 |         paginateParams.offset = params?.offset?.toInteger() ?: 0 | 
|---|
| [144] | 199 |  | 
|---|
| [479] | 200 |         def sort = "task." + (params?.sort ?: "attentionFlag") | 
|---|
 | 201 |         def order = params?.order == "asc" ? "asc" : "desc" | 
|---|
 | 202 |         def orderBy = " order by " + sort + ' ' + order | 
|---|
| [476] | 203 |  | 
|---|
| [479] | 204 |         def namedParams = [:] | 
|---|
| [503] | 205 |         namedParams.person = person ?: authService.currentUser | 
|---|
 | 206 |         namedParams.startDate = startDate ?: dateUtilService.today | 
|---|
 | 207 |         namedParams.endDate = endDate ?: dateUtilService.tomorrow | 
|---|
| [165] | 208 |  | 
|---|
| [479] | 209 |         def baseQuery = "from Task as task \ | 
|---|
 | 210 |                                         left join task.assignedPersons as assignedPersonOfTask \ | 
|---|
 | 211 |                                         left join assignedPersonOfTask.person as assignedPerson \ | 
|---|
 | 212 |                                         left join task.assignedGroups as assignedGroupOfTask \ | 
|---|
 | 213 |                                         left join assignedGroupOfTask.personGroup as personGroup \ | 
|---|
 | 214 |                                         left join personGroup.persons as assignedPersonViaGroup \ | 
|---|
| [511] | 215 |                                         left join task.taskModifications as taskModification \ | 
|---|
 | 216 |                                         left join taskModification.person as createdBy \ | 
|---|
 | 217 |                                         left join taskModification.taskModificationType as taskModificationType \ | 
|---|
| [479] | 218 |                                         where (task.trash = false \ | 
|---|
| [511] | 219 |                                                     and task.targetStartDate < :endDate \ | 
|---|
 | 220 |                                                     and task.targetCompletionDate >= :startDate \ | 
|---|
| [479] | 221 |                                                     and ( \ | 
|---|
| [511] | 222 |                                                         (taskModificationType.id = 1 \ | 
|---|
 | 223 |                                                         and createdBy = :person \ | 
|---|
 | 224 |                                                         and task.leadPerson = :person) \ | 
|---|
 | 225 |                                                         or ( \ | 
|---|
 | 226 |                                                             task.approved = true \ | 
|---|
 | 227 |                                                             and ( \ | 
|---|
 | 228 |                                                                 task.leadPerson = :person \ | 
|---|
 | 229 |                                                                 or assignedPerson = :person \ | 
|---|
 | 230 |                                                                 or assignedPersonViaGroup = :person \ | 
|---|
 | 231 |                                                             ) \ | 
|---|
| [479] | 232 |                                                         ) \ | 
|---|
| [511] | 233 |                                                     ) \ | 
|---|
 | 234 |                                         )" | 
|---|
| [479] | 235 |  | 
|---|
 | 236 |         def searchQuery = "select distinct task " + baseQuery + orderBy | 
|---|
 | 237 |         def list = Task.executeQuery(searchQuery, namedParams, paginateParams) | 
|---|
 | 238 |  | 
|---|
 | 239 |         def countQuery = "select count(distinct task) as taskCount " + baseQuery | 
|---|
 | 240 |         def totalCount = Task.executeQuery(countQuery, namedParams)[0].toInteger() | 
|---|
 | 241 |  | 
|---|
 | 242 |         def taskInstanceList = new PagedResultList(list, totalCount) | 
|---|
 | 243 |         return taskInstanceList | 
|---|
| [503] | 244 |     } // getPersonsTasks() | 
|---|
| [479] | 245 |  | 
|---|
 | 246 |     /** | 
|---|
| [503] | 247 |     * Get tasks by budget status, by default planned in the last week. | 
|---|
| [479] | 248 |     * @param params The request params. | 
|---|
| [503] | 249 |     * @param budgetStatus Defaults to planned. | 
|---|
 | 250 |     * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to). | 
|---|
 | 251 |     * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than). | 
|---|
| [479] | 252 |     */ | 
|---|
| [503] | 253 |     def getBudgetTasks(params, budgetStatus=null, startDate=null, endDate=null) { | 
|---|
| [260] | 254 |         params.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax) | 
|---|
| [165] | 255 |         params.offset = params?.offset?.toInteger() ?: 0 | 
|---|
| [476] | 256 |         params.sort = params?.sort ?: "targetStartDate" | 
|---|
 | 257 |         params.order = params?.order ?: "asc" | 
|---|
| [165] | 258 |  | 
|---|
| [503] | 259 |         budgetStatus = budgetStatus ?: TaskBudgetStatus.read(2) // Planned. | 
|---|
 | 260 |         startDate = startDate ?: dateUtilService.today | 
|---|
 | 261 |         endDate = endDate ?: dateUtilService.tomorrow | 
|---|
| [165] | 262 |  | 
|---|
 | 263 |         def taskInstanceList = Task.createCriteria().list( | 
|---|
 | 264 |             max: params.max, | 
|---|
 | 265 |             offset: params.offset, | 
|---|
 | 266 |             sort: params.sort, | 
|---|
 | 267 |             order: params.order) { | 
|---|
| [503] | 268 |                 eq("taskBudgetStatus", budgetStatus) | 
|---|
| [476] | 269 |                 lt("targetStartDate", dateUtilService.tomorrow) | 
|---|
| [479] | 270 |                 ge("targetCompletionDate", dateUtilService.oneWeekAgo) | 
|---|
| [181] | 271 |                 eq("trash", false) | 
|---|
| [483] | 272 |             } // createCriteria | 
|---|
| [503] | 273 |     } // getBudgetTasks() | 
|---|
| [476] | 274 |  | 
|---|
| [479] | 275 |     /** | 
|---|
| [490] | 276 |     * Get work done by person and date. | 
|---|
| [503] | 277 |     * A person ID and date may be specified in params otherwise the current user and today are used. | 
|---|
| [490] | 278 |     * @param params The request params. | 
|---|
 | 279 |     * @returns A map containing entries, totalEntries, startOfDay, person, totalHours, totalMinutes. | 
|---|
 | 280 |     */ | 
|---|
 | 281 |     def getWorkDone(params, locale) { | 
|---|
 | 282 |         def result = [:] | 
|---|
 | 283 |         result.person = params.person?.id ? Person.get(params.person.id.toInteger()) : authService.currentUser | 
|---|
 | 284 |  | 
|---|
 | 285 |         if(params.date_year && params.date_month && params.date_day) | 
|---|
 | 286 |             result.startOfDay = dateUtilService.makeDate(params.date_year, params.date_month, params.date_day) | 
|---|
 | 287 |         else | 
|---|
 | 288 |             result.startOfDay = dateUtilService.today | 
|---|
 | 289 |  | 
|---|
 | 290 |         result.startOfNextDay = result.startOfDay + 1 | 
|---|
 | 291 |  | 
|---|
 | 292 |         def formattedStartOfDay = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.startOfDay) | 
|---|
 | 293 |  | 
|---|
 | 294 |         def getMessage = { Map m -> | 
|---|
 | 295 |             messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale) | 
|---|
 | 296 |         } | 
|---|
 | 297 |  | 
|---|
 | 298 |         result.entries = Entry.createCriteria().list() { | 
|---|
 | 299 |             eq("enteredBy", result.person) | 
|---|
 | 300 |             ge("dateDone", result.startOfDay) | 
|---|
 | 301 |             lt("dateDone", result.startOfNextDay) | 
|---|
 | 302 |             entryType { | 
|---|
 | 303 |                 eq("id", 3L) | 
|---|
 | 304 |             } | 
|---|
 | 305 |         } // createCriteria | 
|---|
 | 306 |  | 
|---|
 | 307 |         result.totalEntries = result.entries.size() | 
|---|
 | 308 |  | 
|---|
 | 309 |         if(result.totalEntries > 0) | 
|---|
 | 310 |             result.message = getMessage(code:"task.search.text.work.done.message", | 
|---|
 | 311 |                                                                 args:[result.person, formattedStartOfDay]) | 
|---|
 | 312 |         else | 
|---|
 | 313 |             result.message = getMessage(code:"task.search.text.work.done.none.found", | 
|---|
 | 314 |                                                                 args:[result.person, formattedStartOfDay]) | 
|---|
 | 315 |  | 
|---|
 | 316 |         result.totalHours = 0 | 
|---|
 | 317 |         result.totalMinutes = 0 | 
|---|
 | 318 |         result.entries.each() { | 
|---|
 | 319 |             result.totalMinutes += (it.durationHour*60) + it.durationMinute | 
|---|
 | 320 |         } | 
|---|
 | 321 |         result.totalHours = (result.totalMinutes / 60).toInteger() | 
|---|
 | 322 |         result.totalMinutes = result.totalMinutes % 60 | 
|---|
 | 323 |  | 
|---|
 | 324 |         return result | 
|---|
| [503] | 325 |     } // getWorkDone() | 
|---|
| [490] | 326 |  | 
|---|
 | 327 | } // end class | 
|---|