Index: /main/waeup.sirp/trunk/src/waeup/sirp/accesscodes/browser.py
===================================================================
--- /main/waeup.sirp/trunk/src/waeup/sirp/accesscodes/browser.py	(revision 5154)
+++ /main/waeup.sirp/trunk/src/waeup/sirp/accesscodes/browser.py	(revision 5155)
@@ -7,5 +7,5 @@
 from waeup.sirp.browser.pages import UniversityPage
 from waeup.sirp.browser.viewlets import (
-    AdminTask, AddActionButton, LeftSidebar, BatchOpButton)
+    AdminTask, AddActionButton, SearchActionButton, LeftSidebar, BatchOpButton)
 from waeup.sirp.interfaces import IWAeUPObject, IUniversity
 
@@ -105,5 +105,51 @@
         self.redirect(self.url(self.context))
         
-    
+class BatchContainerSearchPage(WAeUPPage):
+    grok.name('search')
+    grok.context(IAccessCodeBatchContainer)
+    grok.template('searchpage')
+    grok.require('waeup.manageACBatches')
+
+    title = 'Search Scratch Cards'
+    pnav = 0
+
+    def update(self, search=None, searchterm=None, searchtype=None,
+               entries=None, disable=None, enable=None):
+        self.searchresults = None
+        if search is not None:
+            searchresults = self.context.search(searchterm, searchtype)
+            if len(searchresults) == 0:
+                return
+            self.searchresults = []
+            for result in searchresults:
+                status = u'unused'
+                if result.disabled is True:
+                    status = u'disabled by %s on %s' % (
+                        result.student_id,
+                        result.invalidation_date.strftime('%c')
+                        )
+                elif result.invalidation_date is not None:
+                    status = u'invalidated by %s on %s' % (
+                        result.student_id,
+                        result.invalidation_date.strftime('%c')
+                        )
+                entry = dict(
+                    serial = result.batch_serial,
+                    code = result.representation,
+                    status = status)
+                self.searchresults.append(entry)
+        if entries is None:
+            return
+        if isinstance(entries, basestring):
+            entries = [entries]
+        for entry in entries:
+            if disable is not None:
+                self.context.disable(entry, self.request.principal.id)
+                self.flash('disabled %s' % entry)
+            elif enable is not None:
+                self.context.enable(entry)
+                self.flash('(re-)enabled %s' % entry)
+        return
+
 class BatchContainerBreadcrumb(Breadcrumb):
     """A breadcrumb for ac batch containers.
@@ -114,4 +160,14 @@
     parent_viewname = 'administration'
 
+class BatchContainerSearchBreadcrumb(Breadcrumb):
+    """A breadcrumb for ac batch containers search page.
+    """
+    grok.require('waeup.manageACBatches')
+    grok.context(IAccessCodeBatchContainer)
+    grok.name('search')
+    title = u'Search Scratch Cards'
+    viewname = 'search'
+    parent_viewname = 'index'
+
 class AdminTaskManageACBatches(AdminTask):
     """Entry on administration page that links to batch container.
@@ -141,4 +197,12 @@
     text = 'Reimport Scratch Card Batch'
 
+class SearchAccessCodeButton(SearchActionButton):
+    """Action button on batch container page which links to search.
+    """
+    grok.context(IAccessCodeBatchContainer)
+    grok.view(BatchContainerPage)
+    grok.require('waeup.manageACBatches')
+    text = 'Search Scratch Cards'    
+    
 class ManageAccessCodes(grok.Viewlet):
     """Link on front page to access code management.
Index: /main/waeup.sirp/trunk/src/waeup/sirp/accesscodes/browser_templates/searchpage.pt
===================================================================
--- /main/waeup.sirp/trunk/src/waeup/sirp/accesscodes/browser_templates/searchpage.pt	(revision 5155)
+++ /main/waeup.sirp/trunk/src/waeup/sirp/accesscodes/browser_templates/searchpage.pt	(revision 5155)
@@ -0,0 +1,47 @@
+<h2>Search Scratch Cards</h2>
+
+<form method="POST">
+  <input type="submit" name="search" value="Search" />
+  for scratch cards 
+
+  <select name="searchtype">
+    <option value="stud_id">
+      used by student with ID
+    </option>
+    <option value="pin">with PIN</option>
+    <option value="serial">with serial</option>
+  </select>:
+
+  <input type="text" name="searchterm" />
+  <br />
+  <b>complete numbers only</b>
+  (Student ID means registration number for APP SCs)
+  <br />
+
+  <p>&nbsp;</p>
+  <div tal:condition="view/searchresults">
+    <h3>Search Results</h3>
+    <table>
+      <thead>
+	<tr>
+	  <th>&nbsp;</th>
+	  <th>Serial</th><th>AC</th><th>Status</th>
+	</tr>
+      </thead>
+      <tbody>
+	<tr tal:repeat="item view/searchresults"
+	    tal:attributes="class python: repeat['item'].odd() and 'even' or 'odd'">
+	  <td><input type="checkbox" name="entries"
+	             tal:attributes="value item/code" /></td>
+	  <td tal:content="item/serial">1</td>
+	  <td tal:content="item/code">APP-1-1234567890</td>
+	  <td tal:content="item/status">unused</td>
+	</tr>
+      </tbody>
+    </table>
+
+    <input type="submit" name="disable" value="Disable ACs" />
+    <input type="submit" name="enable" value="Enable ACs" />
+
+  </div>
+</form>
