Index: main/waeup.kofa/trunk/src/waeup/kofa/hostels/catalog.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/hostels/catalog.py	(revision 13315)
+++ main/waeup.kofa/trunk/src/waeup/kofa/hostels/catalog.py	(revision 13316)
@@ -30,6 +30,4 @@
     grok.context(IBed)
 
-    #bed_id = index.Field(attribute='bed_id')
-    #bed_number = index.Field(attribute='bed_number')
     bed_type = index.Field(attribute='bed_type')
     owner = index.Field(attribute='owner')
Index: main/waeup.kofa/trunk/src/waeup/kofa/hostels/container.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/hostels/container.py	(revision 13315)
+++ main/waeup.kofa/trunk/src/waeup/kofa/hostels/container.py	(revision 13316)
@@ -22,4 +22,6 @@
 import pytz
 from datetime import datetime
+from zope.catalog.interfaces import ICatalog
+from zope.component import queryUtility
 from waeup.kofa.hostels.interfaces import IHostelsContainer, IHostel
 from waeup.kofa.utils.logger import Logger
@@ -52,4 +54,17 @@
         return
 
+    def releaseExpiredAllocations(self, n=7):
+        """Release bed if bed allocation has expired. Allocation expires
+        after `n` days if maintenance fee has not been paid.
+        """
+        cat = queryUtility(ICatalog, name='beds_catalog')
+        results = cat.searchResults(owner=(None, None))
+        counter = 0
+        for bed in results:
+            success = bed.releaseBedIfMaintenanceNotPaid(n=n)
+            if success:
+                counter += 1
+        return counter
+
     @property
     def expired(self):
Index: main/waeup.kofa/trunk/src/waeup/kofa/hostels/hostel.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/hostels/hostel.py	(revision 13315)
+++ main/waeup.kofa/trunk/src/waeup/kofa/hostels/hostel.py	(revision 13316)
@@ -253,21 +253,45 @@
         if self.owner == NOT_OCCUPIED:
             return
-        else:
-            old_owner = self.owner
+        old_owner = self.owner
+        self.owner = NOT_OCCUPIED
+        notify(grok.ObjectModifiedEvent(self))
+        accommodation_session = grok.getSite()[
+            'hostels'].accommodation_session
+        try:
+            bedticket = grok.getSite()['students'][old_owner][
+                          'accommodation'][str(accommodation_session)]
+        except KeyError:
+            return '%s without bed ticket' % old_owner
+        bedticket.bed = None
+        tz = getUtility(IKofaUtils).tzinfo
+        timestamp = now(tz).strftime("%Y-%m-%d %H:%M:%S %Z")
+        bedticket.bed_coordinates = u'-- booking cancelled on %s --' % (
+            timestamp,)
+        return old_owner
+
+    def releaseBedIfMaintenanceNotPaid(self, n=7):
+        if self.owner == NOT_OCCUPIED:
+            return
+        accommodation_session = grok.getSite()[
+            'hostels'].accommodation_session
+        try:
+            bedticket = grok.getSite()['students'][self.owner][
+                          'accommodation'][str(accommodation_session)]
+        except KeyError:
+            return
+        if bedticket.maint_payment_made:
+            return
+        jetzt = datetime.utcnow()
+        days_ago = getattr(jetzt - bedticket.booking_date, 'days')
+        if days_ago > n:
             self.owner = NOT_OCCUPIED
             notify(grok.ObjectModifiedEvent(self))
-            accommodation_session = grok.getSite()[
-                'hostels'].accommodation_session
-            try:
-                bedticket = grok.getSite()['students'][old_owner][
-                              'accommodation'][str(accommodation_session)]
-            except KeyError:
-                return '%s without bed ticket' % old_owner
             bedticket.bed = None
             tz = getUtility(IKofaUtils).tzinfo
             timestamp = now(tz).strftime("%Y-%m-%d %H:%M:%S %Z")
-            bedticket.bed_coordinates = u'-- booking cancelled on %s --' % (
+            bedticket.bed_coordinates = u'-- booking expired (%s) --' % (
                 timestamp,)
-            return old_owner
+            return True
+        return
 
     def writeLogMessage(self, view, message):
Index: main/waeup.kofa/trunk/src/waeup/kofa/hostels/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/hostels/interfaces.py	(revision 13315)
+++ main/waeup.kofa/trunk/src/waeup/kofa/hostels/interfaces.py	(revision 13316)
@@ -71,4 +71,9 @@
         """
 
+    def releaseExpiredAllocations(n):
+        """Release bed if bed allocation has expired. Allocation expires
+        after `n` days if maintenance fee has not been paid.
+        """
+
     def writeLogMessage(view, message):
         """Add an INFO message to hostels.log.
@@ -230,4 +235,8 @@
     def switchReservation():
         """Reserves bed or relases reserved bed respectively.
+        """
+
+    def releaseBedIfMaintenanceNotPaid():
+        """Release bed if maintenance fee has not been paid on time.
         """
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/hostels/tests.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/hostels/tests.py	(revision 13315)
+++ main/waeup.kofa/trunk/src/waeup/kofa/hostels/tests.py	(revision 13316)
@@ -35,4 +35,5 @@
 from waeup.kofa.hostels.interfaces import (
     IHostelsContainer, IHostel, IBed)
+from waeup.kofa.hostels.vocabularies import NOT_OCCUPIED
 from waeup.kofa.hostels.container import HostelsContainer
 from waeup.kofa.hostels.hostel import Hostel, Bed
@@ -173,4 +174,35 @@
         clearSite()
         shutil.rmtree(self.dc_root)
+
+class HostelsContainerTests(HostelsFullSetup):
+
+    layer = FunctionalLayer
+
+    def test_release_expired_allocations(self):
+        cat = queryUtility(ICatalog, name='beds_catalog')
+        bedticket = BedTicket()
+        bedticket.booking_session = 2004
+        bedticket.bed_coordinates = u'anything'
+        self.student['accommodation'].addBedTicket(bedticket)
+        self.app[
+            'hostels']['hall-x']['hall_block_room_bed'].owner = self.student_id
+        notify(grok.ObjectModifiedEvent(
+            self.app['hostels']['hall-x']['hall_block_room_bed']))
+        results = cat.searchResults(owner=(self.student_id, self.student_id))
+        self.assertEqual(len(results), 1)
+        released = self.app['hostels'].releaseExpiredAllocations(7)
+        self.assertEqual(released, 0)
+        delta = timedelta(days=10)
+        bedticket.booking_date = datetime.utcnow() - delta
+        released = self.app['hostels'].releaseExpiredAllocations(7)
+        self.assertEqual(released, 1)
+        results = cat.searchResults(owner=(self.student_id, self.student_id))
+        self.assertEqual(len(results), 0)
+        self.assertMatches(bedticket.display_coordinates,
+            '-- booking expired (2015-10-14 08:35:38 UTC) --')
+        self.assertEqual(
+            self.app['hostels']['hall-x']['hall_block_room_bed'].owner,
+            NOT_OCCUPIED)
+        return
 
 class BedCatalogTests(HostelsFullSetup):
@@ -330,5 +362,5 @@
         self.assertMatches(bedticket.bed_coordinates,
           u' -- booking cancelled on <YYYY-MM-DD hh:mm:ss> UTC --')
-        # The catalog was updated.
+        # The catalog has been updated.
         results = cat.searchResults(owner=(self.student_id, self.student_id))
         assert len(results) == 0
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 13315)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 13316)
@@ -706,5 +706,4 @@
     """
     bed = Attribute('The bed object')
-    booking_date = Attribute('Date of booking the bed')
     maint_payment_made = Attribute('True if maintenance payment is made')
 
@@ -731,5 +730,5 @@
         source = academic_sessions_vocab,
         required = True,
-        readonly = True,
+        readonly = False
         )
 
@@ -737,5 +736,5 @@
         title = _(u'Booking Date'),
         required = False,
-        readonly = True,
+        readonly = False,
         )
 
@@ -743,5 +742,5 @@
         title = _(u'Booking Activation Code'),
         required = False,
-        readonly = True,
+        readonly = False,
         )
 
