Index: main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 7014)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 7015)
@@ -20,4 +20,5 @@
 from time import time
 from datetime import date, datetime
+from zope.event import notify
 from zope.catalog.interfaces import ICatalog
 from zope.component import queryUtility
@@ -1073,8 +1074,4 @@
         return
 
-    @grok.action('Book accommodation')
-    def addBedTicket(self, **data):
-        self.redirect(self.url(self.context, '@@add'))
-
     def isStudent(self):
         prm = get_principal_role_manager()
@@ -1090,4 +1087,13 @@
         return sa
 
+class AddBedTicketActionButton(ManageActionButton):
+    grok.order(1)
+    grok.context(IStudentAccommodation)
+    grok.view(AccommodationManageFormPage)
+    grok.require('waeup.handleStudent')
+    icon = 'actionicon_home.png'
+    text = 'Book accommodation'
+    target = 'add'
+
 class BedTicketAddPage(WAeUPPage):
     """ Page to add an online payment ticket
@@ -1109,6 +1115,6 @@
 
     # To be sepezified in customization packages
-    def selectBed(self, student):
-        return getAccommodationDetails(student)
+    def selectBed(self, available_beds):
+        return selectBed(available_beds)
 
     def update(self, SUBMIT=None):
@@ -1116,10 +1122,9 @@
         acc_details  = self.getAccommodationDetails(student)
         if not student.state in acc_details['allowed_states']:
-            self.flash("Wrong state.")
+            self.flash("You are in the wrong registration state.")
             self.redirect(self.url(self.context))
             return
         if str(student['studycourse'].entry_session) in self.context.keys():
-            self.flash('Ticket for %d already created.'
-                % student['studycourse'].entry_session)
+            self.flash('You already booked a bed space for the current accommodation session.')
             self.redirect(self.url(self.context))
             return
@@ -1169,5 +1174,5 @@
         bedticket.booking_session = acc_details['booking_session']
         bedticket.bed_type = acc_details['bt']
-        bed = selectBed(available_beds)
+        bed = self.selectBed(available_beds)
         bed.bookBed(student.student_id)
         bedticket.bed = bed
@@ -1202,4 +1207,73 @@
     def title(self):
         return 'Bed Ticket %s' % self.context.getSessionString()
+
+class RelocateStudentActionButton(ManageActionButton):
+    grok.order(1)
+    grok.context(IBedTicket)
+    grok.view(BedTicketDisplayFormPage)
+    grok.require('waeup.manageHostels')
+    icon = 'actionicon_reload.png'
+    text = 'Relocate student'
+    target = 'relocate'
+
+class BedTicketRelocationPage(grok.View):
+    """ Callback view
+    """
+    grok.context(IBedTicket)
+    grok.name('relocate')
+    grok.require('waeup.manageHostels')
+
+    # To be sepezified in customization packages
+    def getAccommodationDetails(self, student):
+        return getAccommodationDetails(student)
+
+    # To be sepezified in customization packages
+    def selectBed(self, available_beds):
+        return selectBed(available_beds)
+
+    # This update method simulates a valid callback und must be
+    # specified in the customization package. The parameters must be taken
+    # from the incoming request.
+    def update(self):
+        student = self.context.getStudent()
+        acc_details  = self.getAccommodationDetails(student)
+        if acc_details['bt'] == self.context.bed_type:
+            self.flash("Bed category hasn't changed. Student can't be relocated.")
+            return
+        # Search a bed and exit if no bed is found or if the student
+        # has already booked a bed
+        cat = queryUtility(ICatalog, name='beds_catalog', default=None)
+        entries = cat.searchResults(
+            bed_type=(acc_details['bt'],acc_details['bt']))
+        available_beds = [
+            entry for entry in entries if entry.owner == NOT_OCCUPIED]
+        if not available_beds:
+            self.flash('There is no free bed in your category %s.'
+                        % acc_details['bt'])
+            return
+        entries = cat.searchResults(
+            owner=(student.student_id,student.student_id))
+        # Rlease old bed
+        allocated_bed = self.context.bed
+        allocated_bed.owner = NOT_OCCUPIED
+        notify(grok.ObjectModifiedEvent(allocated_bed))
+        # Alocate new bed
+        self.context.bed_type = acc_details['bt']
+        bed = self.selectBed(available_beds)
+        bed.bookBed(student.student_id)
+        self.context.bed = bed
+        hall_title = bed.__parent__.hostel_name
+        coordinates = bed.getBedCoordinates()[1:]
+        block, room_nr, bed_nr = coordinates
+        self.context.bed_coordinates = '%s, Block %s, Room %s, Bed %s' % (
+            hall_title, block, room_nr, bed_nr)
+        self.flash('Student relocated and new bed booked: %s'
+            % self.context.bed_coordinates)
+        self.redirect(self.url(self.context))
+        return
+
+    def render(self):
+        self.redirect(self.url(self.context, '@@index'))
+        return
 
 class StudentHistoryPage(WAeUPPage):
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py	(revision 7014)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py	(revision 7015)
@@ -161,5 +161,5 @@
         self.app['configuration'].addSessionConfiguration(configuration)
 
-        # Create a hostel with bed
+        # Create a hostel with two beds
         hostel = Hostel()
         hostel.hostel_id = u'hall-1'
@@ -171,4 +171,10 @@
         bed.owner = NOT_OCCUPIED
         bed.bed_type = u'regular_male_fr'
+        self.app['hostels'][hostel.hostel_id].addBed(bed)
+        bed = Bed()
+        bed.bed_id = u'hall-1_A_101_B'
+        bed.bed_number = 2
+        bed.owner = NOT_OCCUPIED
+        bed.bed_type = u'regular_female_fr'
         self.app['hostels'][hostel.hostel_id].addBed(bed)
 
@@ -834,5 +840,5 @@
         self.assertMatches('...Course registration has been started...',
                            self.browser.contents)
-        self.assertTrue(self.student.state,'school fee paid')
+        self.assertTrue(self.student.state == 'school fee paid')
         return
 
@@ -858,9 +864,9 @@
         # Managers can use HOS code and book a bed space with it
         self.browser.open(self.acco_student_path)
-        self.browser.getControl("Book accommodation").click()
-        self.assertMatches('...Wrong state...',
+        self.browser.getLink("Book accommodation").click()
+        self.assertMatches('...You are in the wrong...',
                            self.browser.contents)
         IWorkflowInfo(self.student).fireTransition('admit')
-        self.browser.getControl("Book accommodation").click()
+        self.browser.getLink("Book accommodation").click()
         self.assertMatches('...Activation Code:...',
                            self.browser.contents)
@@ -872,8 +878,8 @@
         # Bed has been allocated
         bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
-        self.assertTrue(bed.owner,self.student_id)
+        self.assertTrue(bed.owner == self.student_id)
         # BedTicketAddPage is now blocked
-        self.browser.getControl("Book accommodation").click()
-        self.assertMatches('...Ticket for 2004 already created...',
+        self.browser.getLink("Book accommodation").click()
+        self.assertMatches('...You already booked a bed space...',
             self.browser.contents)
         # The bed ticket displays the data correctly
@@ -884,5 +890,17 @@
         self.assertMatches('...regular_male_fr...', self.browser.contents)
         self.assertMatches('...%s...' % pin, self.browser.contents)
-        # Managers cab delete bed tickets
+        # Managers can relocate students
+        self.browser.getLink("Relocate student").click()
+        self.assertMatches(
+            "...Bed category hasn't changed...", self.browser.contents)
+        self.student.sex = u'f'
+        self.browser.getLink("Relocate student").click()
+        self.assertMatches(
+            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
+        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
+        self.assertTrue(bed1.owner == NOT_OCCUPIED)
+        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
+        self.assertTrue(bed2.owner == self.student_id)
+        # Managers can delete bed tickets
         self.browser.open(self.acco_student_path)
         ctrl = self.browser.getControl(name='val_id')
