Skip to content

Commit

Permalink
GH-751 Add another option in booking settings to allow cancel without…
Browse files Browse the repository at this point in the history
… limit.
  • Loading branch information
georgmaisser committed Dec 7, 2024
1 parent f6ed210 commit b0ba1d2
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 15 deletions.
1 change: 1 addition & 0 deletions classes/booking_option.php
Original file line number Diff line number Diff line change
Expand Up @@ -3122,6 +3122,7 @@ public static function return_cancel_until_date($optionid) {
$allowupdatedays = $bookingsettings->allowupdatedays;
if (
!empty($bookingsettings->cancelrelativedate) &&
$bookingsettings->cancelrelativedate == 1 &&
isset($allowupdatedays) &&
$allowupdatedays != 10000 &&
!empty($starttime)
Expand Down
6 changes: 5 additions & 1 deletion classes/observer.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,14 @@ public static function bookinganswer_cancelled(\mod_booking\event\bookinganswer_
* @throws dml_exception
*/
public static function bookingoption_cancelled(\mod_booking\event\bookingoption_cancelled $event) {

rules_info::$eventstoexecute[] = function () use ($event) {
$optionid = $event->objectid;
$settings = singleton_service::get_instance_of_booking_option_settings($optionid);

// We don't test.
if (PHPUNIT_TEST && empty($settings->cmid)) {
return;
}
$bookingoption = singleton_service::get_instance_of_booking_option($settings->cmid, $optionid);
$bookinganswer = singleton_service::get_instance_of_booking_answers($settings);

Expand Down
4 changes: 4 additions & 0 deletions lang/de/booking.php
Original file line number Diff line number Diff line change
Expand Up @@ -600,13 +600,17 @@
$string['campaignstart'] = 'Beginn der Kampagne';
$string['campaignstart_help'] = 'Wann soll die Kampagne starten?';
$string['campaigntype'] = 'Kampagnentyp';
$string['cancancelbookabsolute'] = 'Stornodatum mit fixem Datum setzen';
$string['cancancelbookallow'] = 'Teilnehmer:innen dürfen Buchungen selbst stornieren';
$string['cancancelbookdays'] = 'Nutzer:innen können nur bis n Tage vor Kursstart stornieren. Negative Werte meinen n Tage NACH Kursstart.';
$string['cancancelbookdays:bookingclosingtime'] = 'Nutzer:innen können nur bis n Tage vor <b>Anmeldeschluss (Buchungsende)</b> stornieren. Negative Werte meinen n Tage NACH Anmeldeschluss.';
$string['cancancelbookdays:bookingopeningtime'] = 'Nutzer:innen können nur bis n Tage vor <b>Anmeldebeginn (Buchungsbeginn)</b> stornieren. Negative Werte meinen n Tage NACH Anmeldebeginn.';
$string['cancancelbookdays:semesterstart'] = 'Nutzer:innen können nur bis n Tage vor <b>Semesterbeginn</b> stornieren. Negative Werte meinen n Tage NACH Semesterbeginn.';
$string['cancancelbookdaysno'] = 'Kein Limit';
$string['cancancelbookrelative'] = 'Stornodatum <b>relativ zu {$a}</b> setzen';
$string['cancancelbooksetting'] = 'Stornobedingen definieren';
$string['cancancelbooksetting_help'] = 'Diese Einstellungen können durch die Einstellugnen in den einzelnen Buchungsoptionen überschrieben werden.';
$string['cancancelbookunlimited'] = 'Stornieren ohne limit möglich.';
$string['cancel'] = 'Abbrechen';
$string['cancelallusers'] = 'Alle gebuchten Teilnehmer:innen stornieren';
$string['cancelbooking'] = 'Buchung stornieren';
Expand Down
6 changes: 5 additions & 1 deletion lang/en/booking.php
Original file line number Diff line number Diff line change
Expand Up @@ -612,13 +612,17 @@
$string['campaignstart'] = 'Start of campaign';
$string['campaignstart_help'] = 'When does the campaign start?';
$string['campaigntype'] = 'Campaign type';
$string['cancancelbookabsolute'] = 'Set cancellation date with a fixed date';
$string['cancancelbookallow'] = 'Allow users to cancel their booking themselves';
$string['cancancelbookdays'] = 'Disallow users to cancel their booking n days before start. Minus means, that users can still cancel n days AFTER course start.';
$string['cancancelbookdays:bookingclosingtime'] = 'Disallow users to cancel their booking n days before <b>registration end</b> (booking closing time). Minus means, that users can still cancel n days AFTER registration end.';
$string['cancancelbookdays:bookingopeningtime'] = 'Disallow users to cancel their booking n days before <b>registration start</b> (booking opening time). Minus means, that users can still cancel n days AFTER registration start.';
$string['cancancelbookdays:semesterstart'] = 'Disallow users to cancel their booking n days before <b>semester</b> start. Minus means, that users can still cancel n days AFTER semester start.';
$string['cancancelbookdaysno'] = "Don't limit";
$string['cancancelbookrelative'] = 'Set cancelling date <b>relative to {$a}</b>';
$string['cancancelbookrelative'] = 'Set cancellation date <b>relative to {$a}</b>';
$string['cancancelbooksetting'] = 'Define cancellation conditions';
$string['cancancelbooksetting_help'] = 'These settings can be overriden by more special settings in the booking options';
$string['cancancelbookunlimited'] = 'Cancellation without limits is possible.';
$string['cancel'] = 'Cancel';
$string['cancelallusers'] = 'Cancel all booked users';
$string['cancelbooking'] = 'Cancel booking';
Expand Down
22 changes: 18 additions & 4 deletions mod_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -779,23 +779,37 @@ public function definition() {
// Cancel date is either absolute or relative to defined start.
$strid = 'cdo:' . $canceldependenton;
$a = get_string($strid, 'mod_booking');
$mform->addElement('advcheckbox', 'cancelrelativedate', get_string('cancancelbookrelative', 'mod_booking', $a));

$canceloptions = [
0 => get_string('cancancelbookabsolute', 'mod_booking'),
1 => get_string('cancancelbookrelative', 'mod_booking', $a),
2 => get_string('cancancelbookunlimited', 'mod_booking'),
];

$mform->addElement(
'select',
'cancelrelativedate',
get_string('cancancelbooksetting', 'booking'),
$canceloptions
);
$mform->addHelpButton('cancelrelativedate', 'cancancelbooksetting', 'mod_booking');

$mform->hideIf('cancelrelativedate', 'cancancelbook', 'eq', 0);
$mform->hideIf('cancelrelativedate', 'disablecancel', 'eq', 1);
$mform->hideIf('cancelrelativedate', 'disablecancel', 'neq', 0);
$mform->setDefault('cancelrelativedate',
(int)booking::get_value_of_json_by_key($bookingid, 'cancelrelativedate') ?? 1);

$mform->addElement('date_time_selector', 'allowupdatetimestamp', get_string('canceldateabsolute', 'mod_booking'));
$mform->hideIf('allowupdatetimestamp', 'cancancelbook', 'eq', 0);
$mform->hideIf('allowupdatetimestamp', 'cancelrelativedate', 'eq', 1);
$mform->hideIf('allowupdatetimestamp', 'cancelrelativedate', 'neq', 0);
$mform->setDefault('allowupdatetimestamp',
booking::get_value_of_json_by_key($bookingid, 'allowupdatetimestamp') ?? '');

$opts = [10000 => get_string('cancancelbookdaysno', 'mod_booking')];
$extraopts = array_combine(range(-100, 100), range(-100, 100));
$opts = $opts + $extraopts;
$mform->addElement('select', 'allowupdatedays', $cancancelbookdaysstring, $opts);
$mform->hideIf('allowupdatedays', 'cancelrelativedate', 'eq', 0);
$mform->hideIf('allowupdatedays', 'cancelrelativedate', 'neq', 1);

$mform->setDefault('allowupdatedays', 10000); // One million means "no limit".
$mform->disabledIf('allowupdatedays', 'cancancelbook', 'eq', 0);
Expand Down
178 changes: 169 additions & 9 deletions tests/bo_availability/condition_allowupdate_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,169 @@ public function test_booking_bookit_allowupdate(array $bdata): void {
$this->setUser($student1);

// Try to book again with user1.
list($id, $isavailable, $description) = $boinfo->is_available($settings->id, $student1->id, true);
[$id, $isavailable, $description] = $boinfo->is_available($settings->id, $student1->id, true);
$this->assertEquals(MOD_BOOKING_BO_COND_ISCANCELLED, $id);

// Now we undo cancel of the booking option.
booking_option::cancelbookingoption($settings->id, '', true);

// Try to book again with user1.
$this->setUser($student1);
list($id, $isavailable, $description) = $boinfo->is_available($settings->id, $student1->id, true);
[$id, $isavailable, $description] = $boinfo->is_available($settings->id, $student1->id, true);
$this->assertEquals(MOD_BOOKING_BO_COND_OPTIONHASSTARTED, $id);
}

/**
* Test cancelation with all cancelrelativedate options.
*
* @covers \condition\iscancelled::is_available
* @covers \condition\hasstarted::is_available
* @param array $bdata
* @throws \coding_exception
* @throws \dml_exception
*
* @dataProvider booking_common_settings_provider
*/
public function test_booking_bookit_cancelrelativedate(array $bdata): void {
global $DB, $CFG;

singleton_service::destroy_instance();

$bdata['cancancelbook'] = 0;
$bdata['allowupdate'] = 1;

// Setup test data.
$course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);

// Create users.
$admin = $this->getDataGenerator()->create_user();
$student1 = $this->getDataGenerator()->create_user();
$student2 = $this->getDataGenerator()->create_user();
$teacher = $this->getDataGenerator()->create_user();
$bookingmanager = $this->getDataGenerator()->create_user(); // Booking manager.

$bdata['course'] = $course->id;
$bdata['bookingmanager'] = $bookingmanager->username;

$bdata['cancancelbook'] = 0;

$bookings = [];
// Booking option where cancel is not allowed.
$bookings[0] = $this->getDataGenerator()->create_module('booking', $bdata);

// Booking option where cancel is allowed.
$bdata['cancancelbook'] = 1;
$bookings[1] = $this->getDataGenerator()->create_module('booking', $bdata);

// Booking option where cancel is allowed until precise date (5 days from now).
// So allowed now.
$bdata['cancancelbook'] = 1;
$bdata['allowupdatetimestamp'] = strtotime('now + 5 days');
$bdata['cancelrelativedate'] = 0;
$bookings[2] = $this->getDataGenerator()->create_module('booking', $bdata);

// Booking option where cancel is allowed until precise date (5 days from now).
// So not allowed now.
$bdata['cancancelbook'] = 1;
$bdata['allowupdatetimestamp'] = strtotime('now - 5 days');
$bdata['cancelrelativedate'] = 0;
$bookings[3] = $this->getDataGenerator()->create_module('booking', $bdata);

// Booking option where cancel is allowed only 20 days before coursestart.
// So, not allowed now.
$bdata['cancancelbook'] = 1;
$bdata['cancelrelativedate'] = 1;
$bdata['allowupdatedays'] = 20;
$bookings[4] = $this->getDataGenerator()->create_module('booking', $bdata);

// Booking option where cancel is allowed only 2 days before coursestart.
// So, allowed now.
$bdata['cancancelbook'] = 1;
$bdata['cancelrelativedate'] = 1;
$bdata['allowupdatedays'] = 2;
$bookings[5] = $this->getDataGenerator()->create_module('booking', $bdata);

// Booking option where cancel is without limit.
// So, allowed now.
$bdata['cancancelbook'] = 1;
$bdata['cancelrelativedate'] = 2;
$bookings[6] = $this->getDataGenerator()->create_module('booking', $bdata);

$this->setAdminUser();

$this->getDataGenerator()->enrol_user($admin->id, $course->id);
$this->getDataGenerator()->enrol_user($student1->id, $course->id);
$this->getDataGenerator()->enrol_user($student2->id, $course->id);
$this->getDataGenerator()->enrol_user($teacher->id, $course->id);
$this->getDataGenerator()->enrol_user($bookingmanager->id, $course->id);

$record1 = new stdClass();
$record1->text = 'Test option1';
$record1->courseid = 0;
$record1->maxanswers = 2;
$record1->coursestarttime = strtotime('now + 10 days');
$record1->courseendtime = strtotime('now + 12 days');

$record2 = new stdClass();
$record2->text = 'Test option1';
$record2->courseid = 0;
$record2->maxanswers = 2;
$record2->coursestarttime = strtotime('now - 10 days');
$record2->courseendtime = strtotime('now + 12 days');

/** @var mod_booking_generator $plugingenerator */
$plugingenerator = self::getDataGenerator()->get_plugin_generator('mod_booking');

$expectedresults1 = [
0 => MOD_BOOKING_BO_COND_ALREADYBOOKED,
1 => MOD_BOOKING_BO_COND_CONFIRMCANCEL,
2 => MOD_BOOKING_BO_COND_CONFIRMCANCEL,
3 => MOD_BOOKING_BO_COND_ALREADYBOOKED,
4 => MOD_BOOKING_BO_COND_ALREADYBOOKED,
5 => MOD_BOOKING_BO_COND_CONFIRMCANCEL,
6 => MOD_BOOKING_BO_COND_CONFIRMCANCEL,
];

$expectedresults2 = [
0 => MOD_BOOKING_BO_COND_ALREADYBOOKED,
1 => MOD_BOOKING_BO_COND_CONFIRMCANCEL,
2 => MOD_BOOKING_BO_COND_CONFIRMCANCEL,
3 => MOD_BOOKING_BO_COND_ALREADYBOOKED,
4 => MOD_BOOKING_BO_COND_ALREADYBOOKED,
5 => MOD_BOOKING_BO_COND_ALREADYBOOKED,
6 => MOD_BOOKING_BO_COND_CONFIRMCANCEL,
];

foreach ($bookings as $key => $booking) {
$this->setAdminUser();
$record1->bookingid = $booking->id;
$record2->bookingid = $booking->id;
$option1 = $plugingenerator->create_option($record1);
$settings1 = singleton_service::get_instance_of_booking_option_settings($option1->id);
$boinfo1 = new bo_info($settings1);

$option2 = $plugingenerator->create_option($record2);
$settings2 = singleton_service::get_instance_of_booking_option_settings($option2->id);
$boinfo2 = new bo_info($settings2);

// Try to book again with user1.
$this->setUser($student1);
$result = booking_bookit::bookit('option', $settings1->id, $student1->id);
$result = booking_bookit::bookit('option', $settings1->id, $student1->id);

$result = booking_bookit::bookit('option', $settings1->id, $student1->id);
[$id, $isavailable, $description] = $boinfo1->is_available($settings1->id, $student1->id, true);
$this->assertEquals($expectedresults1[$key], $id);

$result = booking_bookit::bookit('option', $settings2->id, $student1->id);
$result = booking_bookit::bookit('option', $settings2->id, $student1->id);

$result = booking_bookit::bookit('option', $settings2->id, $student1->id);
[$id, $isavailable, $description] = $boinfo1->is_available($settings2->id, $student1->id, true);
$this->assertEquals($expectedresults2[$key], $id);
}
}

/**
* Test isbookable, bookitbutton, alreadybooked.
*
Expand Down Expand Up @@ -183,7 +334,7 @@ public function test_booking_bookit_isbookable(array $bdata): void {
$this->setUser($student1);

// Try to book again with user1.
list($id, $isavailable, $description) = $boinfo1->is_available($settings1->id, $student1->id, true);
[$id, $isavailable, $description] = $boinfo1->is_available($settings1->id, $student1->id, true);
$this->assertEquals(MOD_BOOKING_BO_COND_ISBOOKABLE, $id);

// Now we enable option1 back.
Expand All @@ -195,13 +346,13 @@ public function test_booking_bookit_isbookable(array $bdata): void {

// Try to book again with user1.
$this->setUser($student1);
list($id, $isavailable, $description) = $boinfo1->is_available($settings1->id, $student1->id, true);
[$id, $isavailable, $description] = $boinfo1->is_available($settings1->id, $student1->id, true);
$this->assertEquals(MOD_BOOKING_BO_COND_BOOKITBUTTON, $id);

// That was just for fun. Now we make sure the student1 will be booked.
$result = booking_bookit::bookit('option', $settings1->id, $student1->id);
$result = booking_bookit::bookit('option', $settings1->id, $student1->id);
list($id, $isavailable, $description) = $boinfo1->is_available($settings1->id, $student1->id, true);
[$id, $isavailable, $description] = $boinfo1->is_available($settings1->id, $student1->id, true);
$this->assertEquals(MOD_BOOKING_BO_COND_ALREADYBOOKED, $id);
}

Expand Down Expand Up @@ -297,13 +448,13 @@ public function test_booking_bookit_subbookings(array $bdata): void {

$this->setUser($student1);
// Validate that subboking is available and non-bloking.
list($id, $isavailable, $description) = $boinfo1->is_available($settings1->id, $student1->id, false);
[$id, $isavailable, $description] = $boinfo1->is_available($settings1->id, $student1->id, false);
$this->assertEquals(MOD_BOOKING_BO_COND_SUBBOOKING, $id);

// Book option1 by student1.
$result = booking_bookit::bookit('option', $settings1->id, $student1->id);
$result = booking_bookit::bookit('option', $settings1->id, $student1->id);
list($id, $isavailable, $description) = $boinfo1->is_available($settings1->id, $student1->id, true);
[$id, $isavailable, $description] = $boinfo1->is_available($settings1->id, $student1->id, true);
$this->assertEquals(MOD_BOOKING_BO_COND_ALREADYBOOKED, $id);

// Set blocking subbooking.
Expand Down Expand Up @@ -338,13 +489,13 @@ public function test_booking_bookit_subbookings(array $bdata): void {
// Try to book option2 with student2.
$this->setUser($student2);
// Validate that subboking is available and non-bloking.
list($id, $isavailable, $description) = $boinfo2->is_available($settings2->id, $student2->id, false);
[$id, $isavailable, $description] = $boinfo2->is_available($settings2->id, $student2->id, false);
$this->assertEquals(MOD_BOOKING_BO_COND_SUBBOOKINGBLOCKS, $id);

// Book option2 by student2.
$result = booking_bookit::bookit('option', $settings2->id, $student2->id);
$result = booking_bookit::bookit('option', $settings2->id, $student2->id);
list($id, $isavailable, $description) = $boinfo2->is_available($settings2->id, $student2->id, true);
[$id, $isavailable, $description] = $boinfo2->is_available($settings2->id, $student2->id, true);
$this->assertEquals(MOD_BOOKING_BO_COND_ALREADYBOOKED, $id);
// TODO: how to make subboking in code?
Expand Down Expand Up @@ -378,4 +529,13 @@ public static function booking_common_settings_provider(): array {
];
return ['bdata' => [$bdata]];
}

/**
* Mandatory clean-up after each test.
*/
public function tearDown(): void {
parent::tearDown();
// Mandatory clean-up.
singleton_service::destroy_instance();
}
}

0 comments on commit b0ba1d2

Please sign in to comment.