Index: trunk/grails-app/controllers/CostCodeDetailedController.groovy
===================================================================
--- trunk/grails-app/controllers/CostCodeDetailedController.groovy	(revision 441)
+++ trunk/grails-app/controllers/CostCodeDetailedController.groovy	(revision 441)
@@ -0,0 +1,110 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+@Secured(['ROLE_AppAdmin', 'ROLE_Manager', 'ROLE_InventoryManager'])
+class CostCodeDetailedController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        params.max = Math.min( params.max ? params.max.toInteger() : 10,  100)
+        [ costCodeInstanceList: CostCode.list( params ), costCodeInstanceTotal: CostCode.count() ]
+    }
+
+    def show = {
+
+        // In the case of an actionSubmit button, rewrite action name from 'index'.
+        if(params._action_Show)
+            params.action='show'
+
+        def costCodeInstance = CostCode.get( params.id )
+
+        if(!costCodeInstance) {
+            flash.message = "CostCode not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ costCodeInstance : costCodeInstance ] }
+    }
+
+    def delete = {
+        def costCodeInstance = CostCode.get( params.id )
+        if(costCodeInstance) {
+            try {
+                costCodeInstance.delete(flush:true)
+                flash.message = "CostCode ${params.id} deleted"
+                redirect(action:list)
+            }
+            catch(org.springframework.dao.DataIntegrityViolationException e) {
+                flash.message = "CostCode ${params.id} could not be deleted"
+                redirect(action:show,id:params.id)
+            }
+        }
+        else {
+            flash.message = "CostCode not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+
+        // In the case of an actionSubmit button, rewrite action name from 'index'.
+        if(params._action_Edit)
+            params.action='edit'
+
+        def costCodeInstance = CostCode.get( params.id )
+
+        if(!costCodeInstance) {
+            flash.message = "CostCode not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ costCodeInstance : costCodeInstance ]
+        }
+    }
+
+    def update = {
+        def costCodeInstance = CostCode.get( params.id )
+        if(costCodeInstance) {
+            if(params.version) {
+                def version = params.version.toLong()
+                if(costCodeInstance.version > version) {
+                    
+                    costCodeInstance.errors.rejectValue("version", "default.optimistic.locking.failure")
+                    render(view:'edit',model:[costCodeInstance:costCodeInstance])
+                    return
+                }
+            }
+            costCodeInstance.properties = params
+            if(!costCodeInstance.hasErrors() && costCodeInstance.save(flush: true)) {
+                flash.message = "CostCode ${params.id} updated"
+                redirect(action:show,id:costCodeInstance.id)
+            }
+            else {
+                render(view:'edit',model:[costCodeInstance:costCodeInstance])
+            }
+        }
+        else {
+            flash.message = "CostCode not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def create = {
+        def costCodeInstance = new CostCode()
+        costCodeInstance.properties = params
+        return ['costCodeInstance':costCodeInstance]
+    }
+
+    def save = {
+        def costCodeInstance = new CostCode(params)
+        if(!costCodeInstance.hasErrors() && costCodeInstance.save(flush: true)) {
+            flash.message = "CostCode ${costCodeInstance.id} created"
+            redirect(action:show,id:costCodeInstance.id)
+        }
+        else {
+            render(view:'create',model:[costCodeInstance:costCodeInstance])
+        }
+    }
+}
Index: trunk/grails-app/controllers/InventoryItemDetailedController.groovy
===================================================================
--- trunk/grails-app/controllers/InventoryItemDetailedController.groovy	(revision 440)
+++ trunk/grails-app/controllers/InventoryItemDetailedController.groovy	(revision 441)
@@ -34,5 +34,5 @@
 
     /**
-    * Disaply the import view.
+    * Display the import view.
     */
     def importInventory = {
@@ -90,4 +90,26 @@
         def s = inventoryCsvService.buildInventory(inventoryItemList)
         render s
+    }
+
+    /**
+    * Display the import view for purchases.
+    */
+    def importInventoryItemPurchases = {
+    }
+
+    /**
+    * Handle the inventory purchases import save.
+    */
+    def importInventoryItemPurchasesSave = {
+        def result = inventoryCsvService.importInventoryItemPurchases(request)
+
+        if(!result.error) {
+            flash.message = g.message(code: "inventory.import.success")
+            redirect(action:search)
+            return
+        }
+
+        flash.errorMessage = g.message(code: result.error.code, args: result.error.args)
+        redirect(action: importInventoryItemPurchases)
     }
 
@@ -194,4 +216,6 @@
                                     inventoryMovementListTotal: result.inventoryMovementListTotal,
                                     inventoryMovementListMax: result.inventoryMovementListMax,
+                                    inventoryItemPurchases: result.inventoryItemPurchases,
+                                    inventoryItemPurchasesTotal: result.inventoryItemPurchasesTotal,
                                     showTab: result.showTab]
 
Index: trunk/grails-app/controllers/InventoryItemPurchaseDetailedController.groovy
===================================================================
--- trunk/grails-app/controllers/InventoryItemPurchaseDetailedController.groovy	(revision 441)
+++ trunk/grails-app/controllers/InventoryItemPurchaseDetailedController.groovy	(revision 441)
@@ -0,0 +1,180 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+@Secured(['ROLE_AppAdmin', 'ROLE_Manager', 'ROLE_InventoryManager'])
+class InventoryItemPurchaseDetailedController extends BaseController {
+
+    def authService
+    def inventoryPurchaseService
+
+    def index = {
+        redirect(controller: 'inventoryItemDetailed', action:'search', params:params)
+    }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def show = {
+        def inventoryItemPurchaseInstance = InventoryItemPurchase.read( params.id )
+
+        if(!inventoryItemPurchaseInstance) {
+            flash.message = "InventoryItemPurchase not found with id ${params.id}"
+            redirect(controller: 'inventoryItemDetailed', action:'search')
+        }
+        else { return [ inventoryItemPurchaseInstance : inventoryItemPurchaseInstance ] }
+    }
+
+    def delete = {
+        def result = inventoryPurchaseService.delete(params)
+
+        if(!result.error) {
+            flash.message = g.message(code: "default.delete.success", args: ["InventoryItemPurchase", params.id])
+            redirect(controller: 'inventoryItemDetailed',
+                            action: 'show',
+                            id: result.inventoryItemId,
+                            params: [showTab: "showPurchasingTab"])
+            return
+        }
+
+        flash.errorMessage = g.message(code: result.error.code, args: result.error.args)
+
+        if(result.error.code == "default.not.found") {
+            redirect(controller: 'inventoryItemDetailed', action: 'search')
+            return
+        }
+
+        redirect(action:show, id: params.id)
+    }
+
+    def edit = {
+        def result = inventoryPurchaseService.edit(params)
+
+        if(!result.error)
+            return [ inventoryItemPurchaseInstance : result.inventoryItemPurchaseInstance ]
+
+        flash.errorMessage = g.message(code: result.error.code, args: result.error.args)
+        redirect(controller: 'inventoryItemDetailed', action:'search', params:params)
+    }
+
+    def update = {
+        def result = inventoryPurchaseService.update(params)
+
+        if(!result.error) {
+            flash.message = g.message(code: "default.update.success", args: ["Inventory Purchase", params.id])
+            redirect(action:show, id: params.id)
+            return
+        }
+
+        if(result.error.code == "default.not.found") {
+            flash.message = g.message(code: result.error.code, args: result.error.args)
+            redirect(controller: 'inventoryItemDetailed', action:'search', params:params)
+            return
+        }
+
+        render(view:'edit', model:[inventoryItemPurchaseInstance: result.inventoryItemPurchaseInstance.attach()])
+    }
+
+    def create = {
+        def inventoryItemPurchaseInstance = new InventoryItemPurchase()
+        inventoryItemPurchaseInstance.properties = params
+
+        if(!inventoryItemPurchaseInstance.inventoryItem) {
+            flash.message = "Please select an inventory item then the 'purchasing' tab."
+            redirect(controller: 'inventoryItemDetailed', action: 'search')
+            return
+        }
+
+        return ['inventoryItemPurchaseInstance':inventoryItemPurchaseInstance]
+    }
+
+    def save = {
+        def result = inventoryPurchaseService.save(params)
+
+        if(!result.error) {
+            flash.message = g.message(code: "default.create.success", args: ["Inventory Purchase", ''])
+            redirect(controller: 'inventoryItemDetailed',
+                            action: 'show',
+                            id: result.inventoryItemId,
+                            params: [showTab: "showPurchasingTab"])
+            return
+        }
+
+        render(view:'create', model:['inventoryItemPurchaseInstance': result.inventoryItemPurchaseInstance])
+    }
+
+    def receive = {
+        def inventoryItemPurchaseInstance = InventoryItemPurchase.read( params.id )
+
+        if(!inventoryItemPurchaseInstance) {
+            flash.message = "InventoryItemPurchase not found with id ${params.id}"
+            redirect(controller: 'inventoryItemDetailed', action:'search')
+            return
+        }
+
+        inventoryItemPurchaseInstance.properties = params
+        def calcQuantities = inventoryPurchaseService.calcQuantities(inventoryItemPurchaseInstance)
+        inventoryItemPurchaseInstance.quantity = calcQuantities.thisOrderRemaining
+        return ['inventoryItemPurchaseInstance':inventoryItemPurchaseInstance,
+                        'orderId': inventoryItemPurchaseInstance.id]
+    }
+
+    def receiveSave = {
+        def result = inventoryPurchaseService.receiveSave(params)
+
+        if(!result.error) {
+            flash.message = g.message(code: "default.create.success", args: ["Inventory Purchase", ''])
+            redirect(controller: 'inventoryItemDetailed',
+                            action: 'show',
+                            id: result.inventoryItemId,
+                            params: [showTab: "showPurchasingTab"])
+            return
+        }
+
+        flash.errorMessage = g.message(code: result.error.code, args: result.error.args)
+
+        if(result.error.code == "default.not.found") {
+            redirect(controller: 'inventoryItemDetailed', action: 'search')
+            return
+        }
+
+        render(view:'receive',
+                        model:['inventoryItemPurchaseInstance': result.inventoryItemPurchaseInstance,
+                                    'orderId': result.orderId])
+    }
+
+    def approveInvoicePayment = {
+        def inventoryItemPurchaseInstance = InventoryItemPurchase.read( params.id )
+
+        if(!inventoryItemPurchaseInstance) {
+            flash.message = "InventoryItemPurchase not found with id ${params.id}"
+            redirect(controller: 'inventoryItemDetailed', action:'search')
+            return
+        }
+
+        inventoryItemPurchaseInstance.properties = params
+        return ['inventoryItemPurchaseInstance':inventoryItemPurchaseInstance,
+                        'orderId': inventoryItemPurchaseInstance.id]
+    }
+
+    def approveInvoicePaymentSave = {
+        def result = inventoryPurchaseService.approveInvoicePaymentSave(params)
+
+        if(!result.error) {
+            flash.message = g.message(code: "default.create.success", args: ["Inventory Purchase", ''])
+            redirect(controller: 'inventoryItemDetailed',
+                            action: 'show',
+                            id: result.inventoryItemId,
+                            params: [showTab: "showPurchasingTab"])
+            return
+        }
+
+        if(result.error.code == "default.not.found") {
+            flash.errorMessage = g.message(code: result.error.code, args: result.error.args)
+            redirect(controller: 'inventoryItemDetailed', action: 'search')
+            return
+        }
+
+        render(view:'approveInvoicePayment',
+                    model:['inventoryItemPurchaseInstance': result.inventoryItemPurchaseInstance,
+                                    'orderId': result.orderId])
+    }
+}
