Index: trunk/grails-app/controllers/TaskDetailedController.groovy
===================================================================
--- trunk/grails-app/controllers/TaskDetailedController.groovy	(revision 489)
+++ trunk/grails-app/controllers/TaskDetailedController.groovy	(revision 490)
@@ -748,4 +748,21 @@
 
     }
+
+    /**
+    * Render a users total work done hours.
+    */
+    @Secured(['ROLE_AppAdmin', 'ROLE_Manager', 'ROLE_TaskManager', 'ROLE_TaskUser'])
+    def workDone = {
+        def result = taskSearchService.getWorkDone(params, RCU.getLocale(request))
+
+        params.message = result.message
+
+        return[entries: result.entries,
+                    totalEntries : result.totalEntries,
+                    startOfDay: result.startOfDay,
+                    person: result.person,
+                    totalHours: result.totalHours,
+                    totalMinutes: result.totalMinutes]
+    } // workDone
 
     /**
Index: trunk/grails-app/i18n/messages.properties
===================================================================
--- trunk/grails-app/i18n/messages.properties	(revision 489)
+++ trunk/grails-app/i18n/messages.properties	(revision 490)
@@ -288,4 +288,8 @@
 task.search.text.planners.range.none.found=No tasks found in the past week or two weeks ahead.
 task.search.calendar.text.too.many.results=Too many results, only {0} are shown.
+task.search.text.work.done=Work Done
+task.search.text.work.done.none.found=No entries found for {0} on {1}.
+task.search.text.work.done.description=Work done by person and date.
+task.search.text.work.done.message=Work done by {0} on {1}.
 
 # InventoryItemSearch
Index: trunk/grails-app/services/TaskSearchService.groovy
===================================================================
--- trunk/grails-app/services/TaskSearchService.groovy	(revision 489)
+++ trunk/grails-app/services/TaskSearchService.groovy	(revision 490)
@@ -11,4 +11,6 @@
     def dateUtilService
     def messageSource
+
+    def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
 
     def paramsMax = 100000
@@ -317,3 +319,56 @@
             } // createCriteria
     }
-}
+
+    /**
+    * Get work done by person and date.
+    * A person ID and date may be specified in params otherwise the currentUser 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
+    }
+
+} // end class
Index: trunk/grails-app/views/taskDetailed/_quickSearchPane.gsp
===================================================================
--- trunk/grails-app/views/taskDetailed/_quickSearchPane.gsp	(revision 489)
+++ trunk/grails-app/views/taskDetailed/_quickSearchPane.gsp	(revision 490)
@@ -93,4 +93,17 @@
                 </tr>
 
+                <tr class="prop">
+                    <td valign="top" class="name">
+                        <label>Links:</label>
+                    </td>
+                    <td valign="top" class="value">
+                        <g:link controller="taskDetailed"
+                                        action="workDone">
+                            <g:message code="task.search.text.work.done" />
+                        </g:link> - <g:message code="task.search.text.work.done.description" />
+                        <br />
+                    </td>
+                </tr>
+
             </tbody>
         </table>
Index: trunk/grails-app/views/taskDetailed/workDone.gsp
===================================================================
--- trunk/grails-app/views/taskDetailed/workDone.gsp	(revision 490)
+++ trunk/grails-app/views/taskDetailed/workDone.gsp	(revision 490)
@@ -0,0 +1,114 @@
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Work Done</title>
+        <resource:dateChooser />
+    </head>
+    <body>
+        <div class="nav">
+            <h1>Work Done</h1>
+        </div>
+        <div class="body">
+            <g:render template="/shared/messages" />
+
+            <div class="paginateButtons">
+                Results: ${totalEntries}
+                <span class="searchButtons">
+                    <a href='' onclick="showElement('searchPane'); return false;">Quick</a>
+                </span>
+            </div>
+
+            <br />
+
+                <g:if test="${entries.size() > 0}">
+                            <div class="list">
+                                <table>
+                                    <thead>
+                                        <tr>
+                                            <th>Task</th>
+                                            <th>Work Done</th>
+                                            <th>Duration</th>
+                                            <th></th>
+                                        </tr>
+                                    </thead>
+                                    <tbody>
+                                        <g:each in="${entries}" status="i" var="entry">
+                                                <tr class="${(i % 2) == 0 ? 'clickableOdd' : 'clickableEven'}"/>
+
+                                                    <td width="35%" onclick='window.location = "${request.getContextPath()}/entryDetailed/edit/${entry.id}"'>
+                                                        ${entry.task.encodeAsHTML()}
+                                                    </td>
+                                                    <td width="60%" onclick='window.location = "${request.getContextPath()}/entryDetailed/edit/${entry.id}"'>
+                                                        ${entry.comment.encodeAsHTML()}
+                                                    </td>
+                                                    <td onclick='window.location = "${request.getContextPath()}/entryDetailed/edit/${entry.id}"'>
+                                                        ${entry.durationHour}:<g:formatNumber number="${entry.durationMinute}" type="number" minIntegerDigits="2" />
+                                                    </td>
+
+                                                    <td class="notClickable">
+                                                        <g:link controller="entryDetailed" action="edit" id="${entry.id}">
+                                                            <img  src="${resource(dir:'images/skin',file:'database_edit.png')}" alt="Edit" />
+                                                        </g:link>
+                                                    </td>
+
+                                                </tr>
+                                        </g:each>
+                                        <tr class="total">
+                                            <td>
+                                                Total
+                                            </td>
+                                            <td>
+                                            </td>
+                                            <td>
+                                                ${totalHours}:<g:formatNumber number="${totalMinutes}" type="number" minIntegerDigits="2" />
+                                            </td>
+                                            <td>
+                                            </td>
+                                        </tr>
+                                    </tbody>
+                                </table>
+                            </div>
+                        </g:if>
+
+            <!-- Start Search Pane -->
+            <div class="overlayPane" id="searchPane" style="display:none;">
+                <h2>Search</h2>
+                <g:form method="post" controller="taskDetailed">
+                    <table>
+                        <tbody>
+
+                            <tr class="prop">
+                                <td valign="top" class="name">Person:</td>
+                                <td valign="top" class="value">
+                                    <g:select optionKey="id"
+                                                        from="${Person.findAllByIsActive(true).sort { p1, p2 -> p1.firstName.compareToIgnoreCase(p2.firstName) }}"
+                                                        name="person.id"
+                                                        value="${person.id}" >
+                                    </g:select>
+                                </td>
+                            </tr>
+
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="date">Date:</label>
+                                </td>
+                                <td valign="top" class="value">
+                                    <richui:dateChooser name="date" format="dd-MM-yyyy" value="${startOfDay}" />
+                                </td>
+                            </tr>
+
+                        </tbody>
+                    </table>
+                    <div class="buttons">
+                        <span class="button">
+                            <g:actionSubmit class="save" value="Update" action="workDone" />
+                            <g:actionSubmit class="cancel" value="${g.message(code:'fp.tag.filterPane.button.cancel.text', default:'Cancel')}" onclick="return hideElement('searchPane');" />
+                        </span>
+                    </div>
+                </g:form>
+            </div> <!-- end search pane -->
+
+        </div> <!--body-->
+    </body>
+</html>
