Index: trunk/grails-app/services/AssetReportService.groovy
===================================================================
--- trunk/grails-app/services/AssetReportService.groovy	(revision 735)
+++ trunk/grails-app/services/AssetReportService.groovy	(revision 740)
@@ -300,3 +300,91 @@
     } // getEquipmentRegister
 
+
+    /**
+    * Selects and returns assets regulatory requirements as specified in recurring regulatory tasks.
+    * @param params The request params, may contain params to specify the search.
+    * @param locale The locale to use when generating result.message.
+    */
+    def getRegulatoryRequirements(params, locale) {
+        def result = [:]
+
+        def fail = { Map m ->
+            result.error = [ code: m.code, args: [] ]
+            return result
+        }
+
+        result.section = Section.get(params.section.id.toLong())
+        result.site = result.section.site
+
+        result.startDate = params.startDate ?: dateUtilService.oneWeekAgo
+        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.startDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.startDate)
+        result.endDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.endDate)
+
+        result.summary = "This report only selects primary assets and not associated assets. \n"
+        result.summary += "Tasks must have a recurrence enabled and regulatory requirement set."
+
+        // Subquery to count subTasks..
+        def subTaskQ = new HqlBuilder().query {
+            select 'count(subTask)'
+            from 'task.subTasks as subTask'
+            where 'subTask.trash = false'
+                and 'subTask.targetStartDate < :endDate'
+                and 'subTask.targetCompletionDate >= :startDate'
+        }
+        def subTaskTotalQ = subTaskQ.query
+
+        subTaskQ.and 'subTask.taskStatus.id = 3' // Complete.
+        def subTaskCompletedQ = subTaskQ.query
+
+        def regulatoryTaskQ = new HqlBuilder().query {
+            select 'new map(primaryAsset.name as assetName',
+                        'primaryAsset.description as assetDescription',
+                        'primaryAsset.isActive as assetIsActive',
+                        'task.id as taskId',
+                        'task.description as taskDescription',
+                        "($subTaskTotalQ) as subTaskTotalCount",
+                        "($subTaskCompletedQ) as subTaskCompletedCount)"
+                        namedParams.startDate = result.startDate
+                        namedParams.endDate = result.endDate
+            from 'Task task',
+                    'left join task.primaryAsset as primaryAsset',
+                    'left join task.taskRecurringSchedule as taskRecurringSchedule'
+            where 'task.regulatoryRequirement = true'
+                and 'taskRecurringSchedule.enabled = true'
+                and 'task.trash = false'
+                        namedParams.sectionId = result.section.id
+                and 'primaryAsset.section.id = :sectionId'
+        }
+        result.tasks = Task.executeQuery(regulatoryTaskQ.query, regulatoryTaskQ.namedParams)
+
+        // Build the report table row for each task.
+        result.tasks.each { task ->
+
+            // Caluculate percentages and build description.
+            def percentComplete
+            def completionFigures
+            if(task.subTaskTotalCount) {
+                percentComplete = (task.subTaskCompletedCount / task.subTaskTotalCount) * 100
+                task.completionFigures = "$percentComplete% ($task.subTaskCompletedCount/$task.subTaskTotalCount)"
+            }
+            else
+                task.completionFigures = '0 sub tasks in date range'
+        } // tasks.each
+
+        result.dataList = result.tasks
+        result.dataList.sort { p1, p2 -> p1.assetName.compareToIgnoreCase(p2.assetName) }
+
+        // Success.
+        return result
+
+    } // getRegulatoryRequirements
+
 } // end class
