Index: main/waeup.sirp/trunk/src/waeup/sirp/students/accommodation.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/accommodation.py	(revision 6990)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/accommodation.py	(revision 6992)
@@ -17,4 +17,5 @@
 Container which contains the (student) accommodation objects.
 """
+from datetime import datetime
 import grok
 from grok import index
@@ -57,4 +58,5 @@
     def __init__(self):
         super(BedTicket, self).__init__()
+        self.booking_date = datetime.now()
         return
 
@@ -63,2 +65,17 @@
 
 BedTicket = attrs_to_fields(BedTicket)
+
+# Bed tickets must be importable. So we might need a factory.
+class BedTicketFactory(grok.GlobalUtility):
+    """A factory for bed tickets.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.BedTicket')
+    title = u"Create a new bed ticket.",
+    description = u"This factory instantiates new bed ticket instances."
+
+    def __call__(self, *args, **kw):
+        return BedTicket()
+
+    def getInterfaces(self):
+        return implementedBy(BedTicket)
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 6990)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 6992)
@@ -40,11 +40,13 @@
     IStudentAccommodation, IStudentClearanceEdit, IStudentStudyLevel,
     ICourseTicket, ICourseTicketAdd, IStudentPaymentsContainer,
-    IStudentOnlinePayment
+    IStudentOnlinePayment, IBedTicket
     )
 from waeup.sirp.students.catalog import search
-from waeup.sirp.students.workflow import CLEARANCE, RETURNING, CLEARED
+from waeup.sirp.students.workflow import (
+    CLEARANCE, RETURNING, CLEARED, REGISTERED, VALIDATED)
 from waeup.sirp.students.studylevel import StudentStudyLevel, CourseTicket
 from waeup.sirp.students.vocabularies import StudyLevelSource
 from waeup.sirp.students.utils import getPaymentDetails
+from waeup.sirp.students.utils import getAccommodationDetails
 from waeup.sirp.browser.resources import toggleall
 from waeup.sirp.authentication import get_principal_role_manager
@@ -846,5 +848,5 @@
         deleted = []
         for id in child_id:
-            # Students are not allowed to remove payment used tickets
+            # Students are not allowed to remove used payment tickets
             if not self.unremovable(self.context[id]):
                 try:
@@ -971,18 +973,138 @@
         return
 
-class AccommodationDisplayFormPage(WAeUPDisplayFormPage):
-    """ Page to display the student accommodation data
+# We don't need the display form page yet
+#class AccommodationDisplayFormPage(WAeUPDisplayFormPage):
+#    """ Page to display the student accommodation data
+#    """
+#    grok.context(IStudentAccommodation)
+#    grok.name('xxx')
+#    grok.require('waeup.viewStudent')
+#    form_fields = grok.AutoFields(IStudentAccommodation)
+#    #grok.template('accommodationpage')
+#    title = 'Accommodation'
+#    pnav = 4
+
+#    @property
+#    def label(self):
+#        return '%s: Accommodation Data' % self.context.__parent__.fullname
+
+# This manage form page is for both students and students officers.
+class AccommodationManageFormPage(WAeUPEditFormPage):
+    """ Page to manage the bed tickets
     """
     grok.context(IStudentAccommodation)
     grok.name('index')
-    grok.require('waeup.viewStudent')
+    grok.require('waeup.handleStudent')
     form_fields = grok.AutoFields(IStudentAccommodation)
-    #grok.template('accommodationpage')
+    grok.template('accommodationmanagepage')
     title = 'Accommodation'
     pnav = 4
 
+    def unremovable(self):
+        prm = get_principal_role_manager()
+        roles = [x[0] for x in prm.getRolesForPrincipal(self.request.principal.id)]
+        return ('waeup.Student' in roles)
+
+    def formatDatetime(self,datetimeobj):
+        if isinstance(datetimeobj, datetime):
+            return datetimeobj.strftime("%Y-%m-%d %H:%M:%S")
+        else:
+            return None
+
     @property
     def label(self):
-        return '%s: Accommodation Data' % self.context.__parent__.fullname
+        return '%s: Accommodation' % self.context.__parent__.fullname
+
+    def update(self):
+        super(AccommodationManageFormPage, self).update()
+        datatable.need()
+        return
+
+    # We need an event handler which releases the bed space too.
+    @grok.action('Remove selected tickets')
+    def delBedTicket(self, **data):
+        form = self.request.form
+        if form.has_key('val_id'):
+            child_id = form['val_id']
+        else:
+            self.flash('No bed ticket selected.')
+            self.redirect(self.url(self.context))
+            return
+        if not isinstance(child_id, list):
+            child_id = [child_id]
+        deleted = []
+        for id in child_id:
+            # Students are not allowed to remove bed tickets
+            if not self.unremovable():
+                try:
+                    del self.context[id]
+                    deleted.append(id)
+                except:
+                    self.flash('Could not delete %s: %s: %s' % (
+                            id, sys.exc_info()[0], sys.exc_info()[1]))
+        if len(deleted):
+            self.flash('Successfully removed: %s' % ', '.join(deleted))
+            write_log_message(self,'removed: % s' % ', '.join(deleted))
+        self.redirect(self.url(self.context))
+        return
+
+    @grok.action('Add bed ticket')
+    def addBedTicket(self, **data):
+        self.redirect(self.url(self.context, '@@add'))
+
+class BedTicketAddPage(WAeUPPage):
+    """ Page to add an online payment ticket
+    """
+    grok.context(IStudentAccommodation)
+    grok.name('add')
+    grok.require('waeup.handleStudent')
+    grok.template('enterpin')
+    ac_prefix = 'CLR'
+    label = 'Add bed ticket'
+    title = 'Add bed ticket'
+    pnav = 4
+    buttonname = 'Create bed ticket'
+
+    # To be sepezified in customization packages
+    def getAccommodationDetails(self, student):
+        return getAccommodationDetails(student)
+
+    def update(self, SUBMIT=None):
+        (booking_session, booking_fee, maint_fee, allowed_states,
+            error)  = self.getAccommodationDetails(self.context.__parent__)
+        if not self.context.getStudent().state in allowed_states:
+            self.flash("Wrong state.")
+            self.redirect(self.url(self.context))
+            return
+        self.ac_series = self.request.form.get('ac_series', None)
+        self.ac_number = self.request.form.get('ac_number', None)
+
+        if SUBMIT is None:
+            return
+        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
+        code = get_access_code(pin)
+        if not code:
+            self.flash('Activation code is invalid.')
+            return
+        # Mark pin as used (this also fires a pin related transition)
+        if code.state == USED:
+            self.flash('Activation code has already been used.')
+            return
+        else:
+            comment = u"AC invalidated for %s" % self.context.getStudent().student_id
+            # Here we know that the ac is in state initialized so we do not
+            # expect an exception, but the owner might be different
+            if not invalidate_accesscode(
+                pin,comment,self.context.getStudent().student_id):
+                self.flash('You are not the owner of this access code.')
+                return
+        bedticket = createObject(u'waeup.BedTicket')
+        #self.applyData(bedticket, **data)
+        bedticket.booking_code = pin
+        bedticket.bed = u'Test Bed'
+        self.context['test'] = bedticket
+        self.flash('Bed ticket created.')
+        self.redirect(self.url(self.context))
+        return
 
 class StudentHistoryPage(WAeUPPage):
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/accommodationmanagepage.pt
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/accommodationmanagepage.pt	(revision 6992)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/accommodationmanagepage.pt	(revision 6992)
@@ -0,0 +1,44 @@
+<h2 i18n:translate=""
+tal:condition="view/label"
+tal:content="view/label">Label</h2>
+
+<div class="form-status"
+    tal:define="status view/status"
+    tal:condition="status">
+    Form Status:
+    <span i18n:translate="" tal:content="view/status">
+      Form status summary
+    </span>
+</div>
+
+<form action="." tal:attributes="action request/URL" method="POST"
+      class="edit-form" enctype="multipart/form-data"> 
+
+  <table class="display dataTableManage">
+    <thead>
+    <tr>
+      <th>&nbsp;</th>
+      <th>Id</th>
+    </tr>
+    </thead>
+    <tbody>
+      <tr tal:repeat="cl context/values">
+         <td>
+          <input type="checkbox"
+    		 name="val_id"
+    		 tal:attributes="value cl/__name__"
+         tal:condition="not: view/unremovable" />
+        </td>
+        <td> <a tal:attributes="href cl/__name__">
+        <span tal:content="cl/__name__">PID</span></a></td>
+      </tr>
+    </tbody>
+  </table>
+
+  <div class="actionButtons" tal:condition="view/availableActions">
+    <span tal:repeat="action view/actions"
+          tal:omit-tag="">
+      <input tal:replace="structure action/render"/>
+    </span>
+  </div>
+</form>
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py	(revision 6990)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py	(revision 6992)
@@ -320,4 +320,57 @@
     """
 
+    bed = schema.TextLine(
+        title = u'Bed',
+        default = None,
+        required = False,
+        )
+
+    booking_session = schema.Choice(
+        title = u'Session',
+        source = academic_sessions_vocab,
+        default = None,
+        required = True,
+        )
+
+    booking_date = schema.Datetime(
+        title = u'Booking Date',
+        required = False,
+        readonly = False,
+        )
+
+    booking_code = schema.TextLine(
+        title = u'Booking Activation Code',
+        default = u'',
+        required = False,
+        readonly = False,
+        )
+
+    booking_fee =schema.Int(
+        title = u'Booking Fee',
+        default = 0,
+        required = False,
+        readonly = False,
+        )
+
+    maint_date = schema.Datetime(
+        title = u'Maintenance Fee Payment Date',
+        required = False,
+        readonly = False,
+        )
+
+    maint_code = schema.TextLine(
+        title = u'Maintenance Fee Payment Code',
+        default = u'Certificate XYZ',
+        required = False,
+        readonly = False,
+        )
+
+    maint_fee = schema.Int(
+        title = u'Maintenance Fee',
+        default = 0,
+        required = False,
+        readonly = False,
+        )
+
 class IStudentPaymentsContainer(IPaymentsContainer):
     """A container for student payment objects.
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/utils.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/utils.py	(revision 6990)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/utils.py	(revision 6992)
@@ -47,2 +47,20 @@
     return (amount, p_item, p_session,
         surcharge_1, surcharge_2, surcharge_3, error)
+
+# To be specified in customization packages.
+# This function is for demonstration and testing only.
+def getAccommodationDetails(student):
+    bookin_fee = maint_fee = 0
+    error = u''
+    booking_session = getSite()['configuration'].accommodation_session
+    allowed_states = getSite()['configuration'].accommodation_states
+    session = str(booking_session)
+    try:
+        academic_session = getSite()['configuration'][session]
+    except KeyError:
+        error = u'Session configuration object is not available.'
+        return (booking_session, booking_fee, maint_fee, allowed_states,
+            error)
+    booking_fee = academic_session.booking_fee
+    maint_fee = academic_session.maint_fee
+    return booking_session, booking_fee, maint_fee, allowed_states, error
