Index: main/waeup.kofa/trunk/src/waeup/kofa/students/accommodation.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/accommodation.py	(revision 13455)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/accommodation.py	(revision 13457)
@@ -133,5 +133,9 @@
                 entry for entry in entries if entry.owner == NOT_OCCUPIED]
             if available_beds:
-                new_bed = students_utils.selectBed(available_beds)
+                new_bed = students_utils.selectBed(
+                    available_beds, self.__parent__.desired_hostel)
+                if new_bed is None:
+                    return False, _(
+                        'There is no free bed in your desired hostel.')
                 new_bed.bookBed(student.student_id)
             else:
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 13455)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 13457)
@@ -1928,5 +1928,23 @@
     grok.template('accommodationmanagepage')
     pnav = 4
-    officers_only_actions = [_('Remove selected')]
+    with_hostel_selection = True
+
+    @property
+    def actionsgroup1(self):
+        if not self.with_hostel_selection:
+            return []
+        students_utils = getUtility(IStudentsUtils)
+        acc_details  = students_utils.getAccommodationDetails(self.context.student)
+        error_message = students_utils.checkAccommodationRequirements(
+            self.context.student, acc_details)
+        if error_message:
+            return []
+        return [_('Save')]
+
+    @property
+    def actionsgroup2(self):
+        if getattr(self.request.principal, 'user_type', None) == 'student':
+            return [_('Book accommodation')]
+        return [_('Book accommodation'), _('Remove selected')]
 
     @property
@@ -1934,4 +1952,27 @@
         return _('${a}: Accommodation',
             mapping = {'a':self.context.__parent__.display_fullname})
+
+    def getHostels(self):
+        """Get a list of all stored hostels.
+        """
+        yield(dict(name=None, title='--', selected=''))
+        for val in grok.getSite()['hostels'].values():
+            selected = ''
+            if val.hostel_id == self.context.desired_hostel:
+                selected = 'selected'
+            yield(dict(name=val.hostel_id, title=val.hostel_name,
+                       selected=selected))
+
+    @action(_('Save'), style='primary')
+    def save(self):
+        hostel = self.request.form.get('hostel', None)
+        self.context.desired_hostel = hostel
+        self.flash(_('Your selection has been saved.'))
+        return
+
+    @jsaction(_('Book accommodation'), style='primary')
+    def bookAccommodation(self, **data):
+        self.redirect(self.url(self.context, 'add'))
+        return
 
     @jsaction(_('Remove selected'))
@@ -1962,11 +2003,4 @@
         self.redirect(self.url(self.context))
         return
-
-    @property
-    def selected_actions(self):
-        if getattr(self.request.principal, 'user_type', None) == 'student':
-            return [action for action in self.actions
-                    if not action.label in self.officers_only_actions]
-        return self.actions
 
 class BedTicketAddPage(KofaPage):
@@ -2030,5 +2064,13 @@
             if available_beds:
                 students_utils = getUtility(IStudentsUtils)
-                bed = students_utils.selectBed(available_beds)
+                bed = students_utils.selectBed(
+                    available_beds, self.context.desired_hostel)
+                if bed is None:
+                    self.flash(_(
+                        'There is no free bed in your desired hostel. '
+                        'Please try another hostel.'),
+                        type="warning")
+                    self.redirect(self.url(self.context))
+                    return
                 # Safety belt for paranoids: Does this bed really exist
                 # in portal?
@@ -2045,4 +2087,5 @@
                 self.flash(_('There is no free bed in your category ${a}.',
                     mapping = {'a':acc_details['bt']}), type="warning")
+                self.redirect(self.url(self.context))
                 return
         if self.with_ac:
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/accommodationmanagepage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/accommodationmanagepage.pt	(revision 13455)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/accommodationmanagepage.pt	(revision 13457)
@@ -1,4 +1,34 @@
 <form action="." tal:attributes="action request/URL" method="POST"
       i18n:domain="waeup.kofa" enctype="multipart/form-data">
+
+  <tal:desired_hostel condition="view/actionsgroup1">
+    <br />
+    <p i18n:translate="">
+    Select and save the <strong>desired hostel</strong> before booking acommodation.
+    Your choice cannot be changed afterwards.
+    </p>
+    <div class="form-inline">
+      <br />
+      <div class="form-group">
+        <select class="form-control" id="hostel" name="hostel">
+          <option tal:repeat="hostel view/getHostels"
+                  tal:attributes="value hostel/name; selected hostel/selected">
+          <span tal:replace="hostel/title">USERTITLE
+          </span>
+          </option>
+        </select>
+      </div>
+      <div class="form-group">
+        <span tal:repeat="action view/actions" tal:omit-tag="">
+          <input tal:condition="python:action.label in view.actionsgroup1"
+                 tal:replace="structure action/render"/>
+        </span>
+      </div>
+    </div>
+    <br />
+  </tal:desired_hostel>
+
+  <h3 i18n:domain="waeup.kofa" i18n:translate="">Bookings</h3>
+  <br />
 
   <table class="kofa-data-table dataTableManage">
@@ -28,9 +58,9 @@
   </table>
 
-  <div tal:condition="view/availableActions">
-    <span tal:repeat="action view/selected_actions"
-          tal:omit-tag="">
-      <input tal:replace="structure action/render"/>
-    </span>
-  </div>
+  <span tal:repeat="action view/actions"
+        tal:omit-tag="">
+    <input tal:condition="python:action.label in view.actionsgroup2"
+           tal:replace="structure action/render"/>
+  </span>
+
 </form>
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 13455)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 13457)
@@ -251,5 +251,4 @@
     phone = PhoneNumber(
         title = _(u'Phone'),
-        description = u'',
         required = False,
         )
@@ -697,4 +696,9 @@
     """
 
+    desired_hostel = schema.TextLine(
+        title = _(u'Desired Hostel'),
+        required = False,
+        )
+
     def addBedTicket(bedticket):
         """Add a bed ticket object.
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 13455)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 13457)
@@ -1203,5 +1203,5 @@
         # Managers can use HOS code and book a bed space with it
         self.browser.open(self.acco_path)
-        self.browser.getLink("Book accommodation").click()
+        self.browser.getControl("Book accommodation").click()
         self.assertMatches('...You are in the wrong...',
                            self.browser.contents)
@@ -1210,10 +1210,10 @@
         # are in accommodation session
         self.student['studycourse'].current_session = 2003
-        self.browser.getLink("Book accommodation").click()
+        self.browser.getControl("Book accommodation").click()
         self.assertMatches('...Your current session does not match...',
                            self.browser.contents)
         self.student['studycourse'].current_session = 2004
         # All requirements are met and ticket can be created
-        self.browser.getLink("Book accommodation").click()
+        self.browser.getControl("Book accommodation").click()
         self.assertMatches('...Activation Code:...',
                            self.browser.contents)
@@ -1227,5 +1227,5 @@
         self.assertTrue(bed1.owner == self.student_id)
         # BedTicketAddPage is now blocked
-        self.browser.getLink("Book accommodation").click()
+        self.browser.getControl("Book accommodation").click()
         self.assertMatches('...You already booked a bed space...',
             self.browser.contents)
@@ -3008,4 +3008,16 @@
 
     def test_student_accommodation(self):
+        # Create a second hostel with one bed
+        hostel = Hostel()
+        hostel.hostel_id = u'hall-2'
+        hostel.hostel_name = u'Hall 2'
+        self.app['hostels'].addHostel(hostel)
+        bed = Bed()
+        bed.bed_id = u'hall-2_A_101_A'
+        bed.bed_number = 1
+        bed.owner = NOT_OCCUPIED
+        bed.bed_type = u'regular_female_fr'
+        self.app['hostels'][hostel.hostel_id].addBed(bed)
+
         self.browser.open(self.login_path)
         self.browser.getControl(name="form.login").value = self.student_id
@@ -3032,5 +3044,5 @@
         # ... but not if booking period has expired ...
         self.app['hostels'].enddate = datetime.now(pytz.utc)
-        self.browser.getLink("Book accommodation").click()
+        self.browser.getControl("Book accommodation").click()
         self.assertMatches('...Outside booking period: ...',
                            self.browser.contents)
@@ -3038,14 +3050,25 @@
         # ... or student data are incomplete ...
         self.student['studycourse'].current_level = None
-        self.browser.getLink("Book accommodation").click()
+        self.browser.getControl("Book accommodation").click()
         self.assertMatches('...Your data are incomplete...',
             self.browser.contents)
         self.student['studycourse'].current_level = 100
         # ... or student is not the an allowed state ...
-        self.browser.getLink("Book accommodation").click()
+        self.browser.getControl("Book accommodation").click()
         self.assertMatches('...You are in the wrong...',
                            self.browser.contents)
+        # Students can still not see the disired hostel selector.
+        self.assertFalse('desired hostel' in self.browser.contents)
         IWorkflowInfo(self.student).fireTransition('admit')
-        self.browser.getLink("Book accommodation").click()
+        # Students can now see the disired hostel selector.
+        self.browser.reload()
+        self.browser.open(self.acco_path)
+        self.assertTrue('desired hostel' in self.browser.contents)
+        self.browser.getControl(name="hostel").value = ['hall-2']
+        self.browser.getControl("Save").click()
+        self.assertTrue('selection has been saved' in self.browser.contents)
+        self.assertTrue('<option selected="selected" value="hall-2">'
+            in self.browser.contents)
+        self.browser.getControl("Book accommodation").click()
         self.assertMatches('...Activation Code:...',
                            self.browser.contents)
@@ -3058,4 +3081,14 @@
         # ... or ACs owned by somebody else.
         ac.owner = u'Anybody'
+        self.browser.getControl(name="ac_series").value = sfeseries
+        self.browser.getControl(name="ac_number").value = sfenumber
+        self.browser.getControl("Create bed ticket").click()
+        # Hostel 2 has only a bed for women.
+        self.assertTrue('There is no free bed in your desired hostel'
+            in self.browser.contents)
+        self.browser.getControl(name="hostel").value = ['hall-1']
+        self.browser.getControl("Save").click()
+        self.browser.getControl("Book accommodation").click()
+        # Student can't use faked ACs ...
         self.browser.getControl(name="ac_series").value = sfeseries
         self.browser.getControl(name="ac_number").value = sfenumber
@@ -3075,5 +3108,5 @@
         self.assertTrue(bed.owner == self.student_id)
         # BedTicketAddPage is now blocked.
-        self.browser.getLink("Book accommodation").click()
+        self.browser.getControl("Book accommodation").click()
         self.assertMatches('...You already booked a bed space...',
             self.browser.contents)
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py	(revision 13455)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py	(revision 13457)
@@ -627,10 +627,20 @@
         return
 
-    def selectBed(self, available_beds):
-        """Select a bed from a list of available beds.
-        In the base configuration we select the first bed found,
-        but can also randomize the selection if we like.
-        """
-        return available_beds[0]
+    def selectBed(self, available_beds, desired_hostel=None):
+        """Select a bed from a filtered list of available beds.
+        In the base configuration beds are sorted by the sort id
+        of the hostel and the bed number. The first bed found in
+        this sorted list is taken.
+        """
+        sorted_beds = sorted(available_beds,
+                key=lambda bed: 1000 * bed.__parent__.sort_id + bed.bed_number)
+        if desired_hostel:
+            # Filter desired hostel beds
+            filtered_beds = [bed for bed in sorted_beds
+                             if bed.bed_id.startswith(desired_hostel)]
+            if not filtered_beds:
+                return
+            return filtered_beds[0]
+        return sorted_beds[0]
 
     def _admissionText(self, student, portal_language):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/viewlets.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/viewlets.py	(revision 13455)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/viewlets.py	(revision 13457)
@@ -501,13 +501,4 @@
         return self.view.url(self.view.context, self.target)
 
-class AddBedTicketActionButton(ManageActionButton):
-    grok.order(1)
-    grok.context(IStudentAccommodation)
-    grok.view(AccommodationManageFormPage)
-    grok.require('waeup.handleAccommodation')
-    icon = 'actionicon_home.png'
-    text = _('Book accommodation')
-    target = 'add'
-
 class BedTicketSlipActionButton(ManageActionButton):
     grok.order(1)
