Index: /trunk/grails-app/controllers/AssetDetailedController.groovy
===================================================================
--- /trunk/grails-app/controllers/AssetDetailedController.groovy	(revision 289)
+++ /trunk/grails-app/controllers/AssetDetailedController.groovy	(revision 290)
@@ -14,9 +14,12 @@
     static allowedMethods = [delete:'POST', save:'POST', update:'POST', saveCopy:'POST']
 
+    def overview = {
+    }
+
+    @Secured(['ROLE_Manager','ROLE_AppAdmin'])
     def importAssetTree = {
     }
-    def overview = {
-    }
-
+
+    @Secured(['ROLE_Manager','ROLE_AppAdmin'])
     def importAssetTreeSave = {
         def result = csvService.importAssetTree(request)
Index: /trunk/grails-app/i18n/messages.properties
===================================================================
--- /trunk/grails-app/i18n/messages.properties	(revision 289)
+++ /trunk/grails-app/i18n/messages.properties	(revision 290)
@@ -1,4 +1,4 @@
 asset.tree.import.success=Asset tree imported.
-asset.tree.import.failure=Could not create asset tree from supplied file.
+asset.tree.import.failure=Could not create asset tree from supplied file, failed on line {0}.
 asset.tree.import.file.over.max.size=Supplied file is greater than max size of {0} Mbytes.
 asset.tree.import.file.not.supplied=No file supplied.
@@ -22,6 +22,5 @@
 person.personGroups.help=Groups may be assigned to tasks and \
 may also provide a record of persons qualified or trained in a specific area. \
-Use Ctrl or Shift to select multiple groups. \
-Groups provide no application authorisations.
+    Groups provide no application authorisations.
 person.loginName=Login Name
 person.loginName.help=This is the id or name that the person will use to login to the application.
Index: /trunk/grails-app/services/AssetSubItemService.groovy
===================================================================
--- /trunk/grails-app/services/AssetSubItemService.groovy	(revision 289)
+++ /trunk/grails-app/services/AssetSubItemService.groovy	(revision 290)
@@ -22,5 +22,5 @@
 
     def save(params) {
-        Asset.withTransaction { status ->
+        AssetSubItem.withTransaction { status ->
             def result = [:]
 
Index: /trunk/grails-app/services/CreateDataService.groovy
===================================================================
--- /trunk/grails-app/services/CreateDataService.groovy	(revision 289)
+++ /trunk/grails-app/services/CreateDataService.groovy	(revision 290)
@@ -156,4 +156,5 @@
         saveAndTest(authInstance)
 
+        // #3
         authInstance = new Authority(description:"Application User, all application users need this base role to allow login.",
                                         authority:"ROLE_AppUser")
Index: /trunk/grails-app/services/CsvService.groovy
===================================================================
--- /trunk/grails-app/services/CsvService.groovy	(revision 289)
+++ /trunk/grails-app/services/CsvService.groovy	(revision 290)
@@ -16,58 +16,85 @@
     */
     def importAssetTree(request) {
-        def result = [:]
-
-        def megaByteMultiplier = 1000 * 1000
-        def fileMaxSize = 10 * megaByteMultiplier //Mb
-
-        def multiPartFile = request.getFile('file')
-
-        InputStreamReader sr = new InputStreamReader(multiPartFile.inputStream)
-        CSVReader reader = new CSVReader(sr)
-
-        def fail = { Map m ->
-            //status.setRollbackOnly()
-            reader.close()
-            result.error = [ code: m.code, args: m.args ]
-            return result
-        }
-
-        if(!multiPartFile || multiPartFile.isEmpty())
-            return fail(code: "asset.tree.import.file.not.supplied")
-
-        if (multiPartFile.getSize() > fileMaxSize)
-            return fail(code: "asset.tree.import.file.over.max.size", args: [fileMaxSize/megaByteMultiplier])
-
-        def line = reader.readNext()
-        def lineNumber = 1
-
-        def header = ["Site", "Section", "Asset", "Sub Asset", "Functional Assembly", "Sub Assembly Group"]
-
-        if(line != header)
-            return fail(code: "asset.tree.import.no.header")
-
-        log.info "Import checks passed, start processing asset file."
-
-        // Prepare the first body line.
-        line = reader.readNext()
-        lineNumber ++
-
-        while(line) {
-            def lineSize = line.size()
-//             log.info lineNumber+ "(" + lineSize + ")" + " : " + line
-
-            if(line[0]) {
-                    if( !Site.findByName(line[0]) )
-                        new Site(name: line[0]).save()
-            }
-
+        Asset.withTransaction { status ->
+            def result = [:]
+
+            def megaByteMultiplier = 1000 * 1000
+            def fileMaxSize = 10 * megaByteMultiplier //Mb
+
+            def multiPartFile = request.getFile('file')
+
+            InputStreamReader sr = new InputStreamReader(multiPartFile.inputStream)
+            CSVReader reader = new CSVReader(sr)
+
+            def fail = { Map m ->
+                status.setRollbackOnly()
+                reader.close()
+                result.error = [ code: m.code, args: m.args ]
+                return result
+            }
+
+            if(!multiPartFile || multiPartFile.isEmpty())
+                return fail(code: "asset.tree.import.file.not.supplied")
+
+            if (multiPartFile.getSize() > fileMaxSize)
+                return fail(code: "asset.tree.import.file.over.max.size", args: [fileMaxSize/megaByteMultiplier])
+
+            def line = reader.readNext()
+            def lineNumber = 1
+
+            def header = ["Site", "Section", "Asset", "Sub Asset", "Functional Assembly", "Sub Assembly Group"]
+
+            if(line != header)
+                return fail(code: "asset.tree.import.no.header")
+
+            log.info "Import checks passed, start processing asset file."
+
+            // Prepare the first body line.
             line = reader.readNext()
             lineNumber ++
-        } //while(line)
-
-        // Success.
-        reader.close()
-        return result
-
+
+            def siteInstance
+            def sectionInstance
+            def assetInstance
+
+            while(line) {
+                def lineSize = line.size()
+    //             log.info lineNumber+ "(" + lineSize + ")" + " : " + line
+
+                if(line[0]) {
+                        if( !Site.findByName(line[0]) )
+                            siteInstance = new Site(name: line[0])
+                            if(!siteInstance.save())
+                                return fail(code: "asset.tree.import.failure", args: [lineNumber])
+                }
+                else continue
+
+                if(line[1]) {
+                        if( !Section.findByName(line[1]) )
+                           sectionInstance =  new Section(name: line[1],
+                                                                                site: siteInstance)
+                            if(!sectionInstance.save())
+                                return fail(code: "asset.tree.import.failure", args: [lineNumber])
+                }
+                else continue
+
+                if(line[2]) {
+                        if( !Asset.findByName(line[2]) )
+                            assetInstance = new Asset(name: line[2],
+                                                                        section: sectionInstance)
+                            if(!sectionInstance.save())
+                                return fail(code: "asset.tree.import.failure", args: [lineNumber])
+                }
+                else continue
+
+                line = reader.readNext()
+                lineNumber ++
+            } //while(line)
+
+            // Success.
+            reader.close()
+            return result
+
+        } //end withTransaction
     } // end importAssetTree()
 
Index: /trunk/grails-app/taglib/CustomTagLib.groovy
===================================================================
--- /trunk/grails-app/taglib/CustomTagLib.groovy	(revision 289)
+++ /trunk/grails-app/taglib/CustomTagLib.groovy	(revision 290)
@@ -36,4 +36,8 @@
         def isChecked, ht, wd, style, html
 
+        def displayFields = attrs.displayFields
+
+        def displayValue = " "
+
         // sets the style to override height and/or width if either of them
         // is specified, else the default from the CSS is taken
@@ -54,4 +58,15 @@
         from.each { obj ->
 
+            displayValue = " "
+
+            if( displayFields?.contains("id") ) {
+                displayValue += obj.id + " - "
+            }
+
+            if(displayFields?.contains("name")) {
+                displayValue += obj.name
+            }
+            else displayValue += obj
+
             // if we wanted to select the checkbox using a click anywhere on the label (also hover effect)
             // but grails does not recognize index suffix in the name as an array:
@@ -61,5 +76,5 @@
             isChecked = (value?.contains(obj."${attrs.optionKey}"))? true: false
 
-            out << "<li>" << checkBox(name:cname, value:obj."${attrs.optionKey}", checked: isChecked) << " ${obj.id} - ${obj.name}" << "</li>"
+            out << "<li>" << checkBox(name:cname, value:obj."${attrs.optionKey}", checked: isChecked) << displayValue << "</li>"
         }
 
Index: /trunk/grails-app/views/assetDetailed/edit.gsp
===================================================================
--- /trunk/grails-app/views/assetDetailed/edit.gsp	(revision 289)
+++ /trunk/grails-app/views/assetDetailed/edit.gsp	(revision 290)
@@ -88,5 +88,6 @@
                                                                     from="${AssetSubItem.list()}"
                                                                     value="${assetInstance?.assetSubItems.collect{it.id}}"
-                                                                    optionKey="id"/>
+                                                                    optionKey="id"
+                                                                    displayFields="['id', 'name']"/>
 
                                 </td>
