From 0798c405219bd6af40549c057cb3428a0907cdf9 Mon Sep 17 00:00:00 2001 From: Moritz Strohm Date: Fri, 6 Jun 2025 09:49:36 +0000 Subject: Allow setting a subsequent time for a resource booking, closes #5127 Closes #5127 Merge request studip/studip!3859 --- app/controllers/course/room_requests.php | 6 +- app/controllers/course/timesrooms.php | 22 ++-- app/controllers/resources/booking.php | 38 ++++-- app/controllers/resources/room_request.php | 100 ++++++++++----- app/views/course/room_requests/_request.php | 2 +- .../course/room_requests/request_show_summary.php | 8 +- app/views/course/timesrooms/editDate.php | 31 +++-- app/views/resources/_common/_request_info.php | 14 ++- app/views/resources/booking/_add_edit_form.php | 9 +- .../resources/room_request/_add_edit_form.php | 7 +- app/views/resources/room_request/resolve.php | 26 ++-- ....1_add_subsequent_time_to_resource_bookings.php | 41 ++++++ lib/models/CourseDate.php | 24 +++- lib/models/resources/BrokenResource.php | 18 ++- lib/models/resources/Building.php | 15 ++- lib/models/resources/Location.php | 15 ++- lib/models/resources/Resource.php | 100 ++++++++++----- lib/models/resources/ResourceBooking.php | 57 ++++++++- lib/models/resources/ResourceLabel.php | 15 ++- lib/models/resources/ResourceRequest.php | 138 +++++++++++++-------- lib/models/resources/Room.php | 10 +- resources/assets/stylesheets/scss/forms.scss | 4 + 22 files changed, 501 insertions(+), 199 deletions(-) create mode 100644 db/migrations/6.0.41.1_add_subsequent_time_to_resource_bookings.php diff --git a/app/controllers/course/room_requests.php b/app/controllers/course/room_requests.php index 97fed50..1fa0282 100644 --- a/app/controllers/course/room_requests.php +++ b/app/controllers/course/room_requests.php @@ -310,6 +310,7 @@ class Course_RoomRequestsController extends AuthenticatedController $this->available_properties = $this->category->getRequestableProperties(); } $this->preparation_time = $this->fromSession('preparation_time'); + $this->subsequent_time = $this->fromSession('subsequent_time'); $this->comment = $this->fromSession('comment'); $this->request->category_id = $this->fromSession('room_category_id'); @@ -531,7 +532,8 @@ class Course_RoomRequestsController extends AuthenticatedController $this->toSession('selected_properties', $this->selected_properties); } - $this->preparation_time = intval($this->request->preparation_time / 60); + $this->preparation_time = $this->request->preparation_time / 60; + $this->subsequent_time = $this->request->subsequent_time / 60; $this->reply_lecturers = $this->request->reply_recipients === ResourceRequest::REPLY_LECTURER; $this->comment = $this->request->comment; @@ -557,7 +559,9 @@ class Course_RoomRequestsController extends AuthenticatedController CSRFProtection::verifyUnsafeRequest(); $this->preparation_time = Request::int('preparation_time', 0); + $this->subsequent_time = Request::int('subsequent_time', 0); $this->request->preparation_time = $this->preparation_time * 60; + $this->request->subsequent_time = $this->subsequent_time * 60; $this->request->comment = Request::get('comment'); if (Request::get('reply_lecturers')) { diff --git a/app/controllers/course/timesrooms.php b/app/controllers/course/timesrooms.php index 8b760dd..209ba9f 100644 --- a/app/controllers/course/timesrooms.php +++ b/app/controllers/course/timesrooms.php @@ -374,9 +374,13 @@ class Course_TimesroomsController extends AuthenticatedController $this->groups = $this->course->statusgruppen; $this->assigned_groups = $this->date->statusgruppen; - $this->preparation_time = $this->date->room_booking instanceof ResourceBooking - ? $this->date->room_booking->preparation_time / 60 - : '0'; + if ($this->date->room_booking instanceof ResourceBooking) { + $this->preparation_time = $this->date->room_booking->preparation_time / 60; + $this->subsequent_time = $this->date->room_booking->subsequent_time / 60; + } else { + $this->preparation_time = 0; + $this->subsequent_time = 0; + } $this->max_preparation_time = Config::get()->RESOURCES_MAX_PREPARATION_TIME; } @@ -506,9 +510,10 @@ class Course_TimesroomsController extends AuthenticatedController if ((Request::option('room') == 'room') || Request::option('room') == 'nochange') { $room_id = null; $preparation_time = Request::int('preparation_time', 0); + $subsequent_time = Request::int('subsequent_time', 0); if (Request::option('room') == 'room') { $room_id = Request::get('room_id'); - if ($preparation_time > $max_preparation_time) { + if ($preparation_time > $max_preparation_time || $subsequent_time > $max_preparation_time) { PageLayout::postError( sprintf( _('Die eingegebene Rüstzeit überschreitet das erlaubte Maximum von %d Minuten!'), @@ -526,7 +531,7 @@ class Course_TimesroomsController extends AuthenticatedController $failure = false; if ($room instanceof Room) { try { - $failure = !$termin->bookRoom($room, $preparation_time ?: 0); + $failure = !$termin->bookRoom($room, $preparation_time, $subsequent_time); } catch (ResourceBookingException|ResourceBookingOverlapException $e) { $course = $e->getRange(); $message_links = []; @@ -601,8 +606,11 @@ class Course_TimesroomsController extends AuthenticatedController '' . htmlReady($termin->getFullName()) . '' )); } - } elseif ($room instanceof Room && $termin->room_booking->preparation_time != ($preparation_time * 60)) { - $termin->bookRoom($room, $preparation_time ?: 0); + } elseif ($room instanceof Room + && ( + $termin->room_booking->preparation_time != ($preparation_time * 60) + || $termin->room_booking->subsequent_time != ($subsequent_time * 60))) { + $termin->bookRoom($room, $preparation_time, $subsequent_time); } } else if ($old_room_id && empty($termin->room_booking->resource_id)) { PageLayout::postInfo( diff --git a/app/controllers/resources/booking.php b/app/controllers/resources/booking.php index b2ab0da..ca025ba 100644 --- a/app/controllers/resources/booking.php +++ b/app/controllers/resources/booking.php @@ -152,6 +152,7 @@ class Resources_BookingController extends AuthenticatedController $this->resource = $this->resource->getDerivedClassInstance(); $this->max_preparation_time = Config::get()->RESOURCES_MAX_PREPARATION_TIME; $this->preparation_time = intval($this->request->preparation_time / 60); + $this->subsequent_time = intval($this->request->subsequent_time / 60); $this->notify_lecturers = '1'; $this->description = ''; $this->internal_comment = ''; @@ -183,7 +184,8 @@ class Resources_BookingController extends AuthenticatedController $this->internal_comment, 0, true, - $this->notify_lecturers + $this->notify_lecturers, + $this->subsequent_time ); if ($bookings && is_array($bookings)) { @@ -301,7 +303,7 @@ class Resources_BookingController extends AuthenticatedController DateTime $begin, DateTime $end, DateTime $repetition_end, - $preparation_time = 0, + int $preparation_time = 0, $description = '', $comment = '', $booking_type = ResourceBooking::TYPE_NORMAL, @@ -310,7 +312,8 @@ class Resources_BookingController extends AuthenticatedController $notification_enabled = false, $included_room_parts = [], $overwrite_bookings = false, - $weekdays = '' + $weekdays = '', + int $subsequent_time = 0 ) { $result = [ @@ -354,7 +357,8 @@ class Resources_BookingController extends AuthenticatedController ? $overwrite_bookings : false ), - $weekdays + $weekdays, + $subsequent_time ); } else { $matching_bookings = ResourceBooking::findByResourceAndTimeRanges( @@ -400,7 +404,8 @@ class Resources_BookingController extends AuthenticatedController ? $overwrite_bookings : false ), - $weekdays + $weekdays, + $subsequent_time ); } } @@ -410,6 +415,7 @@ class Resources_BookingController extends AuthenticatedController $a->begin = $begin->getTimestamp(); $a->end = $end->getTimestamp(); $a->preparation_time = $preparation_time; + $a->subsequent_time = $subsequent_time; $a->description = $description; $a->internal_comment = $comment; $a->booking_type = $booking_type; @@ -471,7 +477,8 @@ class Resources_BookingController extends AuthenticatedController ? $overwrite_bookings : false ), - $weekdays + $weekdays, + $subsequent_time ); $result['bookings'] = [$booking]; } catch (Exception $e) { @@ -519,7 +526,9 @@ class Resources_BookingController extends AuthenticatedController $booking_type == ResourceBooking::TYPE_LOCK ? $overwrite_bookings : false - ) + ), + '', + $subsequent_time ); } catch (ResourceBookingOverlapException $e) { $room_part_errors[] = sprintf( @@ -1020,12 +1029,14 @@ class Resources_BookingController extends AuthenticatedController if ($mode == 'add') { $this->booking_style = $this->booking_style ?: "single"; $this->preparation_time = '0'; + $this->subsequent_time = '0'; $this->block_booking = []; $this->repetition_style = 'weekly'; $this->repetition_interval = '1'; $this->internal_comment = ''; } elseif (($mode == 'edit') || ($mode == 'duplicate')) { $this->preparation_time = $this->booking->preparation_time / 60; + $this->subsequent_time = $this->booking->subsequent_time / 60; $this->block_booking = []; $interval = $this->booking->getRepetitionInterval(); if (!$interval) { @@ -1082,7 +1093,8 @@ class Resources_BookingController extends AuthenticatedController $this->semester_id = Request::get('semester_id'); $this->selected_end = Request::get('selected_end'); - $this->preparation_time = Request::get('preparation_time'); + $this->preparation_time = Request::int('preparation_time', 0); + $this->subsequent_time = Request::int('subsequent_time', 0); $this->notification_enabled = Request::get('notification_enabled'); $this->booking_style = Request::get('booking_style'); $this->block_booking = Request::getArray('block_booking'); @@ -1186,7 +1198,8 @@ class Resources_BookingController extends AuthenticatedController } //Validate the preparation time: - if ($this->preparation_time > $this->max_preparation_time) { + if ($this->preparation_time > $this->max_preparation_time + || $this->subsequent_time > $this->max_preparation_time) { PageLayout::postError( sprintf( _('Es sind maximal %d Minuten für die Rüstzeit erlaubt!'), @@ -1254,7 +1267,7 @@ class Resources_BookingController extends AuthenticatedController return; } - if ($this->preparation_time < 0) { + if ($this->preparation_time < 0 || $this->subsequent_time < 0) { PageLayout::postError( _('Die Rüstzeit darf keinen negativen Wert enthalten!') ); @@ -1414,7 +1427,7 @@ class Resources_BookingController extends AuthenticatedController $interval['begin'], $interval['end'], $this->repetition_end, - intval($this->preparation_time) * 60, + $this->preparation_time * 60, $this->description, $this->internal_comment, $this->booking_type, @@ -1427,7 +1440,8 @@ class Resources_BookingController extends AuthenticatedController : [] ), $this->overwrite_bookings, - $this->repetition_interval === 'workdays' ? '12345' : '' + $this->repetition_interval === 'workdays' ? '12345' : '', + $this->subsequent_time * 60 ); $errors = array_merge($errors, $results['errors']); $room_part_errors = array_merge( diff --git a/app/controllers/resources/room_request.php b/app/controllers/resources/room_request.php index cccd0d6..5c7811b 100644 --- a/app/controllers/resources/room_request.php +++ b/app/controllers/resources/room_request.php @@ -248,8 +248,8 @@ class Resources_RoomRequestController extends AuthenticatedController if (empty($this->filter['request_periods'])) { $sql .= ' OR ( - CAST(resource_requests.begin AS SIGNED) - resource_requests.preparation_time < :semester_end - AND resource_requests.end > :begin + CAST(resource_requests.begin AS SIGNED) - CAST(resource_requests.preparation_time AS SIGNED) < :semester_end + AND CAST(resource_requests.end AS SIGNED) + CAST(resource_requests.subsequent_time AS SIGNED) > :begin )'; } $sql .= ') '; @@ -661,6 +661,7 @@ class Resources_RoomRequestController extends AuthenticatedController $this->end_date_str = $end->format('d.m.Y'); $this->end_time_str = $end->format('H:i'); $this->preparation_time = 0; + $this->subsequent_time = 0; $this->max_preparation_time = $config->RESOURCES_MAX_PREPARATION_TIME; $this->comment = ''; @@ -677,6 +678,7 @@ class Resources_RoomRequestController extends AuthenticatedController $this->end_date_str = Request::get('end_date'); $this->end_time_str = Request::get('end_time'); $this->preparation_time = Request::int('preparation_time', 0); + $this->subsequent_time = Request::int('subsequent_time', 0); if (!$this->begin_date_str) { PageLayout::postError( @@ -702,7 +704,7 @@ class Resources_RoomRequestController extends AuthenticatedController ); return; } - if ($this->preparation_time > $this->max_preparation_time) { + if ($this->preparation_time > $this->max_preparation_time || $this->subsequent_time > $this->max_preparation_time) { PageLayout::postError( sprintf( _('Die eingegebene Rüstzeit überschreitet das erlaubte Maximum von %d Minuten!'), @@ -751,7 +753,8 @@ class Resources_RoomRequestController extends AuthenticatedController $new_begin, $new_end, $this->comment, - $this->preparation_time * 60 + $this->preparation_time * 60, + $this->subsequent_time * 60 ); if ($request) { @@ -870,15 +873,14 @@ class Resources_RoomRequestController extends AuthenticatedController $end->setTimestamp($this->request->end); $config = Config::get(); - $this->begin_date_str = $begin->format('d.m.Y'); - $this->begin_time_str = $begin->format('H:i'); - $this->end_date_str = $end->format('d.m.Y'); - $this->end_time_str = $end->format('H:i'); - $this->preparation_time = 0; + $this->begin_date_str = $begin->format('d.m.Y'); + $this->begin_time_str = $begin->format('H:i'); + $this->end_date_str = $end->format('d.m.Y'); + $this->end_time_str = $end->format('H:i'); + $this->preparation_time = $this->request->preparation_time / 60; + $this->subsequent_time = $this->request->subsequent_time / 60; $this->max_preparation_time = $config->RESOURCES_MAX_PREPARATION_TIME; - $this->comment = ''; - $this->comment = $this->request->comment; - $this->preparation_time = intval($this->request->preparation_time / 60); + $this->comment = $this->request->comment; $this->show_form = true; @@ -888,11 +890,12 @@ class Resources_RoomRequestController extends AuthenticatedController //resource request (or a room request if the resource //is a room). - $this->begin_date_str = Request::get('begin_date'); - $this->begin_time_str = Request::get('begin_time'); - $this->end_date_str = Request::get('end_date'); - $this->end_time_str = Request::get('end_time'); - $this->preparation_time = Request::get('preparation_time'); + $this->begin_date_str = Request::get('begin_date'); + $this->begin_time_str = Request::get('begin_time'); + $this->end_date_str = Request::get('end_date'); + $this->end_time_str = Request::get('end_time'); + $this->preparation_time = Request::int('preparation_time', 0); + $this->subsequent_time = Request::int('subsequent_time', 0); if (!$this->begin_date_str) { PageLayout::postError( @@ -918,7 +921,7 @@ class Resources_RoomRequestController extends AuthenticatedController ); return; } - if ($this->preparation_time > $this->max_preparation_time) { + if ($this->preparation_time > $this->max_preparation_time || $this->subsequent_time > $this->max_preparation_time) { PageLayout::postError( sprintf( _('Die eingegebene Rüstzeit überschreitet das erlaubte Maximum von %d Minuten!'), @@ -960,10 +963,11 @@ class Resources_RoomRequestController extends AuthenticatedController $end_time_arr[2] ?? 0 ); - $this->request->begin = $new_begin->getTimestamp(); - $this->request->end = $new_end->getTimestamp(); - $this->request->comment = $this->comment; + $this->request->begin = $new_begin->getTimestamp(); + $this->request->end = $new_end->getTimestamp(); + $this->request->comment = $this->comment; $this->request->preparation_time = $this->preparation_time * 60; + $this->request->subsequent_time = $this->subsequent_time * 60; if ($this->request->isDirty()) { $successfully_stored = $this->request->store(); @@ -1541,7 +1545,13 @@ class Resources_RoomRequestController extends AuthenticatedController null, 0, $course_date->end_time, - $this->request->preparation_time + $this->request->preparation_time, + '', + '', + ResourceBooking::TYPE_NORMAL, + false, + '', + $this->request->subsequent_time ); if ($booking instanceof ResourceBooking) { $bookings[] = $booking; @@ -1591,7 +1601,13 @@ class Resources_RoomRequestController extends AuthenticatedController null, 0, $date->end_time, - $this->request->preparation_time + $this->request->preparation_time, + '', + '', + ResourceBooking::TYPE_NORMAL, + false, + '', + $this->request->subsequent_time ); if ($booking instanceof ResourceBooking) { $bookings[] = $booking; @@ -1635,7 +1651,13 @@ class Resources_RoomRequestController extends AuthenticatedController null, 0, null, - $this->request->preparation_time + $this->request->preparation_time, + '', + '', + ResourceBooking::TYPE_NORMAL, + false, + '', + $this->request->subsequent_time ); if ($booking instanceof ResourceBooking) { $bookings[] = $booking; @@ -1875,7 +1897,8 @@ class Resources_RoomRequestController extends AuthenticatedController _('Sitzplätze'), _('Wochentag'), _('Uhrzeit'), - _('Rüstzeit'), + _('Rüstzeit vor der Buchung'), + _('Rüstzeit nach der Buchung'), _('Anzahl der Termine'), _('Art der Anfrage'), _('Datum des ersten Termins'), @@ -2001,7 +2024,8 @@ class Resources_RoomRequestController extends AuthenticatedController $room_seats, //Sitzplätze $date_row['day_of_week'], //Wochentag $date_row['time_string'], //Uhrzeit - ($request->preparation_time / 60), //Rüstzeit + ($request->preparation_time / 60), //Rüstzeit vor der Buchung + ($request->subsequent_time / 60), //Rüstzeit nach der Buchung $date_row['date_amount'], //Anzahl Termine $request->getTypeString(true), //Art der Anfrage $date_row['first_date_str'], //Datum des ersten Termins @@ -2177,7 +2201,13 @@ class Resources_RoomRequestController extends AuthenticatedController null, 0, $course_date->end_time, - $this->request->preparation_time + $this->request->preparation_time, + '', + '', + ResourceBooking::TYPE_NORMAL, + false, + '', + $this->request->subsequent_time ); if ($booking instanceof ResourceBooking) { $bookings[] = $booking; @@ -2214,7 +2244,13 @@ class Resources_RoomRequestController extends AuthenticatedController null, 0, $date->end_time, - $this->request->preparation_time + $this->request->preparation_time, + '', + '', + ResourceBooking::TYPE_NORMAL, + false, + '', + $this->request->subsequent_time ); if ($booking instanceof ResourceBooking) { $bookings[] = $booking; @@ -2250,7 +2286,13 @@ class Resources_RoomRequestController extends AuthenticatedController null, 0, null, - $this->request->preparation_time + $this->request->preparation_time, + '', + '', + ResourceBooking::TYPE_NORMAL, + false, + '', + $this->request->subsequent_time ); if ($booking instanceof ResourceBooking) { $bookings[] = $booking; diff --git a/app/views/course/room_requests/_request.php b/app/views/course/room_requests/_request.php index 2ffa282..d53dcc4 100644 --- a/app/views/course/room_requests/_request.php +++ b/app/views/course/room_requests/_request.php @@ -1 +1 @@ -render_partial('resources/_common/_request_info.php') ?> +render_partial('resources/_common/_request_info.php', ['timesrooms_page' => true]) ?> diff --git a/app/views/course/room_requests/request_show_summary.php b/app/views/course/room_requests/request_show_summary.php index 7e4244a..d3f3090 100644 --- a/app/views/course/room_requests/request_show_summary.php +++ b/app/views/course/room_requests/request_show_summary.php @@ -58,11 +58,17 @@ + - +
+ + +