Index: branches/features/purchaseOrders/grails-app/services/CreateDataService.groovy
===================================================================
--- branches/features/purchaseOrders/grails-app/services/CreateDataService.groovy	(revision 897)
+++ branches/features/purchaseOrders/grails-app/services/CreateDataService.groovy	(revision 898)
@@ -17,4 +17,5 @@
     def appConfigService
     def searchableService
+    def purchaseOrderService
     def inventoryItemService
     def assignedGroupService
@@ -1740,7 +1741,5 @@
 
     def createDemoPurchaseOrderNumbers() {
-        for (int i=10000; i<10100; i++) {
-            saveAndTest(new PurchaseOrderNumber(value:"P${i}"))
-        }
+        def r = purchaseOrderService.savePurchaseOrderNumberRange("P", 10000, 10100)
     }
 
Index: branches/features/purchaseOrders/grails-app/services/PurchaseOrderService.groovy
===================================================================
--- branches/features/purchaseOrders/grails-app/services/PurchaseOrderService.groovy	(revision 897)
+++ branches/features/purchaseOrders/grails-app/services/PurchaseOrderService.groovy	(revision 898)
@@ -1,5 +1,44 @@
+/**
+ * Provides a service class for the PurchaseOrder and PurchaseOrderNumber domain classes.
+ */
 class PurchaseOrderService {
 
     boolean transactional = false
+
+    def savePurchaseOrderNumberRange(prefix, startOfRange, endOfRange, suffix="") {
+        PurchaseOrderNumber.withTransaction { status ->
+            def result = [:]
+
+            def fail = { Map m ->
+                status.setRollbackOnly()
+                if(result.purchaseOrderNumber && m.field)
+                    result.purchaseOrderNumber.errors.rejectValue(m.field, m.code)
+                result.error = [ code: m.code, args: ["PurchaseOrderNumber"] ]
+                return result
+            }
+
+            // Sanity checks.
+            if(startOfRange < 0)
+                return fail(code:"default.create.failure")
+            if(endOfRange < 0)
+                return fail(code:"default.create.failure")
+            // Auto swap range.
+            if(endOfRange < startOfRange)
+                (startOfRange, endOfRange) = [endOfRange, startOfRange]
+
+            def r
+            def p = [:]
+            for(i in startOfRange..endOfRange) {
+                p.value= "${prefix}${i}${suffix}"
+                r = savePurchaseOrderNumber(p)
+                if(r.error)
+                    return fail(code: r.code)
+                    
+            }
+
+            // Success.
+            return result
+        }
+    }
 
     PurchaseOrderNumber findNextUnusedPurchaseOrderNumber() {
@@ -37,3 +76,26 @@
         return drafts
     }
+
+    def savePurchaseOrderNumber(params) {
+        PurchaseOrderNumber.withTransaction { status ->
+            def result = [:]
+
+            def fail = { Map m ->
+                status.setRollbackOnly()
+                if(result.purchaseOrderNumber && m.field)
+                    result.purchaseOrderNumber.errors.rejectValue(m.field, m.code)
+                result.error = [ code: m.code, args: ["PurchaseOrderNumber", params.id] ]
+                return result
+            }
+
+            result.purchaseOrderNumber = new PurchaseOrderNumber(params)
+
+            if(result.purchaseOrderNumber.hasErrors() || !result.purchaseOrderNumber.save())
+                return fail(code:"default.create.failure")
+
+            // Success.
+            return result
+        }
+    }
+
 }
