Index: branches/features/purchaseOrders/grails-app/controllers/InventoryItemPurchaseDetailedController.groovy
===================================================================
--- branches/features/purchaseOrders/grails-app/controllers/InventoryItemPurchaseDetailedController.groovy	(revision 889)
+++ branches/features/purchaseOrders/grails-app/controllers/InventoryItemPurchaseDetailedController.groovy	(revision 891)
@@ -11,4 +11,5 @@
     def dateUtilService
     def inventoryPurchaseService
+    def purchaseOrderService
 
     def index = {
@@ -280,6 +281,9 @@
         def costCodes = inventoryPurchaseService.getCostCodesByPerson()
 
+        def purchaseOrderNumbers = [purchaseOrderService.findNextUnusedPurchaseOrderNumber()]
+        purchaseOrderNumbers += purchaseOrderService.findDraftPurchaseOrderNumbers()
+
         return ['inventoryItemPurchaseInstance': inventoryItemPurchaseInstance,
-                        'costCodes': costCodes]
+                        'costCodes': costCodes, purchaseOrderNumbers:purchaseOrderNumbers]
     }
 
Index: branches/features/purchaseOrders/grails-app/domain/InventoryItemPurchase.groovy
===================================================================
--- branches/features/purchaseOrders/grails-app/domain/InventoryItemPurchase.groovy	(revision 889)
+++ branches/features/purchaseOrders/grails-app/domain/InventoryItemPurchase.groovy	(revision 891)
@@ -14,5 +14,5 @@
 
     Integer quantity
-    String purchaseOrderNumber = ''
+    PurchaseOrder purchaseOrder
     BigDecimal orderValueAmount
     Currency orderValueCurrency
@@ -25,24 +25,24 @@
 //     hasMany = []
 
-    static belongsTo = [InventoryItem]
+    static belongsTo = [InventoryItem, PurchaseOrder]
 
     static constraints = {
         quantity(min:0)
-        purchaseOrderNumber(blank:false, maxSize:50, validator: {val, obj ->
-            // For orders the purchaseOrderNumber must be unique for an inventoryItem.
-            if(obj.inventoryItemPurchaseType.id == 1L) {
-                def list = InventoryItemPurchase.withCriteria {
-                    eq('inventoryItem', obj.inventoryItem)
-                    eq('purchaseOrderNumber', obj.purchaseOrderNumber)
-                    eq('inventoryItemPurchaseType', obj.inventoryItemPurchaseType)
-                    if(obj.id)
-                        notEqual('id', obj.id)
-                }
-                if(list.size() > 0)
-                    return 'not.unique.for.inventory.item.order'
-            }
-            // Success.
-            return true
-        })
+//        purchaseOrderNumber(blank:false, maxSize:50, validator: {val, obj ->
+//            // For orders the purchaseOrderNumber must be unique for an inventoryItem.
+//            if(obj.inventoryItemPurchaseType.id == 1L) {
+//                def list = InventoryItemPurchase.withCriteria {
+//                    eq('inventoryItem', obj.inventoryItem)
+//                    eq('purchaseOrderNumber', obj.purchaseOrderNumber)
+//                    eq('inventoryItemPurchaseType', obj.inventoryItemPurchaseType)
+//                    if(obj.id)
+//                        notEqual('id', obj.id)
+//                }
+//                if(list.size() > 0)
+//                    return 'not.unique.for.inventory.item.order'
+//            }
+//            // Success.
+//            return true
+//        })
         invoiceNumber(maxSize:50)
         orderValueAmount(max: new BigDecimal(1000000000000))
@@ -56,3 +56,8 @@
     }
 
+    static transients = [ 'purchaseOrderNumber' ]
+
+    String getPurchaseOrderNumber() {
+        return purchaseOrder?.purchaseOrderNumber?.value
+    }
 }
Index: branches/features/purchaseOrders/grails-app/domain/PurchaseOrder.groovy
===================================================================
--- branches/features/purchaseOrders/grails-app/domain/PurchaseOrder.groovy	(revision 891)
+++ branches/features/purchaseOrders/grails-app/domain/PurchaseOrder.groovy	(revision 891)
@@ -0,0 +1,16 @@
+
+class PurchaseOrder {
+    String comments
+    Supplier supplier
+    Date ordered
+    static hasMany = [inventoryItemPurchases: InventoryItemPurchase]
+
+    static belongsTo = [purchaseOrderNumber: PurchaseOrderNumber]
+
+
+    static constraints = {
+        comments(nullable:true)
+        ordered(nullable:true)
+    }
+
+}
Index: branches/features/purchaseOrders/grails-app/domain/PurchaseOrderNumber.groovy
===================================================================
--- branches/features/purchaseOrders/grails-app/domain/PurchaseOrderNumber.groovy	(revision 891)
+++ branches/features/purchaseOrders/grails-app/domain/PurchaseOrderNumber.groovy	(revision 891)
@@ -0,0 +1,26 @@
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: John
+ * Date: 18/04/2011
+ * Time: 3:01:53 PM
+ * To change this template use File | Settings | File Templates.
+ */
+class PurchaseOrderNumber {
+    String value
+    PurchaseOrder purchaseOrder
+
+    static transients = ['description']
+
+    static constraints = {
+        purchaseOrder(nullable:true)
+    }
+
+    String getDescription() {
+        if (!purchaseOrder) {
+        return value + " - new"
+        } else {
+            return "${value} for ${purchaseOrder.supplier}"
+        }
+    }
+}
Index: branches/features/purchaseOrders/grails-app/services/CreateDataService.groovy
===================================================================
--- branches/features/purchaseOrders/grails-app/services/CreateDataService.groovy	(revision 889)
+++ branches/features/purchaseOrders/grails-app/services/CreateDataService.groovy	(revision 891)
@@ -89,4 +89,5 @@
         createBaseEntryTypes()
 
+
         // Record that data has been created.
         appConfigService.set("baseDataCreated")
@@ -144,4 +145,7 @@
 //         createDemoMaintenanceActions()
         createDemoTaskRecurringSchedules()
+
+        // PurchaseOrderNumbers
+        createDemoPurchaseOrderNumbers()
 
         // Record that data has been created.
@@ -1736,4 +1740,10 @@
     }
 
+    def createDemoPurchaseOrderNumbers() {
+        for (int i=10000; i<10100; i++) {
+            saveAndTest(new PurchaseOrderNumber(value:"P${i}"))
+        }
+    }
+
     /**
     * SearchableIndex and mirroring is disabled at startup.
Index: branches/features/purchaseOrders/grails-app/services/InventoryItemService.groovy
===================================================================
--- branches/features/purchaseOrders/grails-app/services/InventoryItemService.groovy	(revision 889)
+++ branches/features/purchaseOrders/grails-app/services/InventoryItemService.groovy	(revision 891)
@@ -63,5 +63,5 @@
                 }
                 else {
-                    order('purchaseOrderNumber', 'desc')
+                    order('purchaseOrder', 'desc')
                     order('id', 'asc')
                 }
Index: branches/features/purchaseOrders/grails-app/services/InventoryPurchaseService.groovy
===================================================================
--- branches/features/purchaseOrders/grails-app/services/InventoryPurchaseService.groovy	(revision 889)
+++ branches/features/purchaseOrders/grails-app/services/InventoryPurchaseService.groovy	(revision 891)
@@ -6,4 +6,5 @@
     def dateUtilService
     def inventoryMovementService
+    def purchaseOrderService
 
     /**
@@ -23,8 +24,11 @@
         result.totalReceivedAmount = 0
         result.totalPaymentApproved = 0
-        InventoryItemPurchase.withCriteria {
-            eq("inventoryItem", order.inventoryItem)
-            eq("purchaseOrderNumber", order.purchaseOrderNumber)
-        }.each() {
+        def purchaseOrder = order.purchaseOrder
+        def relevantLineItems = purchaseOrder.inventoryItemPurchases.findAll{it.inventoryItem == order.inventoryItem}
+//        InventoryItemPurchase.withCriteria {
+//            eq("inventoryItem", order.inventoryItem)
+//            eq("purchaseOrderNumber", order.purchaseOrderNumber)
+//        }.each() {
+        relevantLineItems.each {
             if(it.inventoryItemPurchaseType.id == 1L) { // Orders.
                 result.totalOrdered += it.quantity
@@ -77,10 +81,10 @@
 
         namedParams.inventoryItem = inventoryItemPurchase.inventoryItem
-        namedParams.purchaseOrderNumber = inventoryItemPurchase.purchaseOrderNumber
+        namedParams.purchaseOrder = inventoryItemPurchase.purchaseOrder
         namedParams.orderPlaced = InventoryItemPurchaseType.read(1)
 
         def order = InventoryItemPurchase.find("from InventoryItemPurchase as p \
                                                                                 where( p.inventoryItem = :inventoryItem \
-                                                                                            and p.purchaseOrderNumber = :purchaseOrderNumber \
+                                                                                            and p.purchaseOrder = :purchaseOrder \
                                                                                             and p.inventoryItemPurchaseType = :orderPlaced )",
                                                                             namedParams)
@@ -322,5 +326,5 @@
 
             result.inventoryItemPurchaseInstance = new InventoryItemPurchase(params)
-            result.inventoryItemPurchaseInstance.purchaseOrderNumber = result.inventoryItemPurchaseInstance.purchaseOrderNumber.trim()
+            result.inventoryItemPurchaseInstance.purchaseOrder = purchaseOrderService.getOrCreatePurchaseOrder(params)
             result.inventoryItemPurchaseInstance.enteredBy = authService.currentUser
             result.inventoryItemPurchaseInstance.inventoryItemPurchaseType = InventoryItemPurchaseType.read(1) // Order
@@ -344,5 +348,5 @@
 
         } // end withTransaction
-    } // save()
+    }  // save()
 
     def receiveSave(params) {
@@ -363,7 +367,10 @@
             result.orderId = order.id
 
+            def purchaseOrderNumber = PurchaseOrderNumber.findByValue(order.purchaseOrderNumber)
+            def purchaseOrder = purchaseOrderNumber.purchaseOrder
+
             result.inventoryItemPurchaseInstance = new InventoryItemPurchase(params)
             result.inventoryItemPurchaseInstance.enteredBy = authService.currentUser
-            result.inventoryItemPurchaseInstance.purchaseOrderNumber = order.purchaseOrderNumber
+            result.inventoryItemPurchaseInstance.purchaseOrder = purchaseOrder
             result.inventoryItemPurchaseInstance.costCode = order.costCode
             result.inventoryItemPurchaseInstance.orderValueCurrency = order.orderValueCurrency
@@ -438,5 +445,5 @@
             result.inventoryItemPurchaseInstance = new InventoryItemPurchase(params)
             result.inventoryItemPurchaseInstance.enteredBy = authService.currentUser
-            result.inventoryItemPurchaseInstance.purchaseOrderNumber = order.purchaseOrderNumber
+            result.inventoryItemPurchaseInstance.purchaseOrder = order.purchaseOrder
             result.inventoryItemPurchaseInstance.costCode = order.costCode
             result.inventoryItemPurchaseInstance.orderValueCurrency = order.orderValueCurrency
Index: branches/features/purchaseOrders/grails-app/services/PurchaseOrderService.groovy
===================================================================
--- branches/features/purchaseOrders/grails-app/services/PurchaseOrderService.groovy	(revision 891)
+++ branches/features/purchaseOrders/grails-app/services/PurchaseOrderService.groovy	(revision 891)
@@ -0,0 +1,43 @@
+class PurchaseOrderService {
+
+    boolean transactional = false
+
+    def serviceMethod() {
+
+    }
+
+    PurchaseOrderNumber findNextUnusedPurchaseOrderNumber() {
+        def empty = PurchaseOrderNumber.list().find{it.purchaseOrder==null}
+        return empty
+    }
+
+    PurchaseOrder getOrCreatePurchaseOrder(params) {
+        def id = params.purchaseOrderNumber.id
+        if (params.purchaseOrderNumber.id instanceof Long) {
+            id = params.purchaseOrderNumber.id
+        } else {
+            id = params.purchaseOrderNumber.id.toString().toLong()
+        }
+        def pon = PurchaseOrderNumber.get(id)
+        def po = pon?.purchaseOrder
+        if (!po) {
+            po = createPurchaseOrder(pon,params)
+        }
+        return po
+    }
+
+    PurchaseOrder createPurchaseOrder(pon,params) {
+        def supplier = Supplier.get(params.supplier.id)
+        def po = new PurchaseOrder(supplier:supplier)
+        pon.purchaseOrder = po
+        po.purchaseOrderNumber = pon
+        pon.save(failOnError:true) // should cascade to save the PO as well. I wonder why it doesn't.
+        po.save(failOnError:true)
+        return po
+    }
+
+    List<PurchaseOrder> findDraftPurchaseOrderNumbers() {
+        def drafts = PurchaseOrder.list().findAll{it.ordered == null}.collect{it.purchaseOrderNumber}
+        return drafts
+    }
+}
Index: branches/features/purchaseOrders/grails-app/views/inventoryItemPurchaseDetailed/create.gsp
===================================================================
--- branches/features/purchaseOrders/grails-app/views/inventoryItemPurchaseDetailed/create.gsp	(revision 889)
+++ branches/features/purchaseOrders/grails-app/views/inventoryItemPurchaseDetailed/create.gsp	(revision 891)
@@ -48,8 +48,14 @@
                             <tr class="prop">
                                 <td valign="top" class="name">
-                                    <label for="purchaseOrderNumber">Purchase Order #:</label>
+                                    <label for="purchaseOrderNumber.id">Purchase Order #:</label>
                                 </td>
                                 <td valign="top" class="value ${hasErrors(bean:inventoryItemPurchaseInstance,field:'purchaseOrderNumber','errors')}">
-                                    <input type="text" maxlength="50" id="purchaseOrderNumber" name="purchaseOrderNumber" value="${fieldValue(bean:inventoryItemPurchaseInstance,field:'purchaseOrderNumber')}"/>
+                                  <g:select from="${purchaseOrderNumbers}"
+                                            optionKey="id"
+                                            optionValue="description"
+                                            name="purchaseOrderNumber.id"
+                                            value="${inventoryItemPurchaseInstance?.purchaseOrder?.purchaseOrderNumber?.id}"
+                                            noSelection="['null':/${g.message(code:'default.please.select.text')}/]">
+                                  </g:select>
                                     <g:helpBalloon code="inventoryItemPurchase.purchaseOrderNumber" />
                                 </td>
Index: branches/features/purchaseOrders/test/unit/PurchaseOrderNumberTests.groovy
===================================================================
--- branches/features/purchaseOrders/test/unit/PurchaseOrderNumberTests.groovy	(revision 891)
+++ branches/features/purchaseOrders/test/unit/PurchaseOrderNumberTests.groovy	(revision 891)
@@ -0,0 +1,48 @@
+import static org.junit.Assert.assertThat
+import static org.hamcrest.CoreMatchers.equalTo
+import grails.test.GrailsUnitTestCase
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: John
+ * Date: 19/04/2011
+ * Time: 8:32:59 AM
+ * To change this template use File | Settings | File Templates.
+ */
+class PurchaseOrderNumberTests extends GrailsUnitTestCase {
+    protected void setUp() {
+        super.setUp()
+        mockDomain(PurchaseOrder)
+        mockDomain(PurchaseOrderNumber)
+    }
+
+    protected void tearDown() {
+        super.tearDown()
+    }
+
+    void testGetDescriptionForNewPurchaseOrder() {
+        createTenPurchaseOrderNumbers()
+        def pon = PurchaseOrderNumber.get(1)
+
+        assertThat pon.description, equalTo("P1000 - new")
+    }
+
+    void testGetDescriptionForExistingPurchaseOrder() {
+        def pon = new PurchaseOrderNumber(value:"P1234").save(failOnError:true)
+        def supplier = new Supplier(name:"Supplier Name",supplierType:new SupplierType())
+        def po = new PurchaseOrder(purchaseOrderNumber:pon, supplier:supplier)
+        pon.purchaseOrder = po
+        pon.save(failOnError:true)
+        po.save(failOnError:true)
+
+        assertThat pon.description, equalTo("P1234 for Supplier Name")
+    }
+
+    private createTenPurchaseOrderNumbers() {
+        for (int i = 1000; i < 1010; i++) {
+            new PurchaseOrderNumber(value: "P${i}").save(failOnError: true)
+        }
+    }
+
+
+}
Index: branches/features/purchaseOrders/test/unit/PurchaseOrderServiceTests.groovy
===================================================================
--- branches/features/purchaseOrders/test/unit/PurchaseOrderServiceTests.groovy	(revision 891)
+++ branches/features/purchaseOrders/test/unit/PurchaseOrderServiceTests.groovy	(revision 891)
@@ -0,0 +1,112 @@
+import grails.test.*
+import static org.junit.Assert.assertThat
+import static org.hamcrest.CoreMatchers.equalTo
+
+class PurchaseOrderServiceTests extends GrailsUnitTestCase {
+    def pos = new PurchaseOrderService()
+
+    protected void setUp() {
+        super.setUp()
+        mockDomain(PurchaseOrder)
+        mockDomain(PurchaseOrderNumber)
+        mockDomain(Supplier)
+        mockDomain(SupplierType)
+    }
+
+    protected void tearDown() {
+        super.tearDown()
+    }
+
+    void testFindsFirstUnusedPurchaseOrderWhenAllUnused() {
+        createTenPurchaseOrderNumbers()
+        assertThat PurchaseOrderNumber.list().size(), equalTo(10)
+
+        def next = pos.findNextUnusedPurchaseOrderNumber()
+
+        assertThat next.value, equalTo("P1000")
+    }
+
+    void testFindsFirstUnusuedPurchaseOrderWhenSomeUsed() {
+        createTenPurchaseOrderNumbers()
+        createPurchaseOrders(4)
+
+        def next = pos.findNextUnusedPurchaseOrderNumber()
+
+        assertThat next.value, equalTo("P1004")
+    }
+
+    void testFindsNullIfNoUnusedPurchaseOrderNumbers() {
+        createTenPurchaseOrderNumbers()
+        createPurchaseOrders(10)
+
+        def next = pos.findNextUnusedPurchaseOrderNumber()
+
+        assertThat next, equalTo(null)
+    }
+
+    void testGetOrCreatePurchaseOrderWithExistingOrder() {
+        createTenPurchaseOrderNumbers()
+        createPurchaseOrders(3)
+        def params=[purchaseOrderNumber:[id:2]]
+
+        def po = pos.getOrCreatePurchaseOrder(params)
+
+        assertThat po.comments, equalTo("Created by test")
+        assertThat po.purchaseOrderNumber.value, equalTo("P1001")
+    }
+
+    void testGetOrCreatePurchaseOrderWithNoExistingOrder() {
+        createTenPurchaseOrderNumbers()
+        createPurchaseOrders(3)
+        createSuppliers(1)
+        def params=[purchaseOrderNumber:[id:4],supplier:[id:1]]
+
+        def po = pos.getOrCreatePurchaseOrder(params)
+
+        assertThat po.comments, equalTo(null)
+        assertThat po.purchaseOrderNumber.value, equalTo("P1003")
+    }
+
+    void testFindsDraftPurchaseOrderNumbers() {
+        createTenPurchaseOrderNumbers()
+        createPurchaseOrders(3)
+        releaseOrder(PurchaseOrder.get(2))
+
+        def drafts = pos.findDraftPurchaseOrderNumbers()
+
+        assertThat drafts.size(), equalTo(2)
+        assertThat drafts[0].value, equalTo("P1000")
+        assertThat drafts[1].value, equalTo("P1002")
+    }
+
+
+
+    def createPurchaseOrders(int howMany) {
+        for (int i=0; i<howMany; i++) {
+            def po = new PurchaseOrder(comments:"Created by test", supplier:new Supplier())
+            def pon = PurchaseOrderNumber.list()[i]
+            pon.purchaseOrder = po;
+            po.purchaseOrderNumber = pon;
+            po.save(failOnError:true)
+            pon.save(failOnError:true)
+        }
+    }
+
+    def createSuppliers(int howMany) {
+        for (int i=0; i<howMany; i++) {
+            def supplier = new Supplier(name:"Supplier ${i}", supplierType:new SupplierType())
+            supplier.save(failOnError:true)
+        }
+    }
+
+    private createTenPurchaseOrderNumbers() {
+        for (int i = 1000; i < 1010; i++) {
+            new PurchaseOrderNumber(value: "P${i}").save(failOnError: true)
+        }
+    }
+
+    def releaseOrder(PurchaseOrder po) {
+        po.ordered = new Date()
+        po.save(failOnError:true)
+    }
+}
