diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d40ef7fd..568c51c54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ # Changelog Fab Manager - An administrator can delete a member +- Ability to configure the duration of a reservation slot. Previously, only 60 minutes slots were allowed - Improved user experience in defining slots in the calendar management - Improved notification email to the member when a rolling subscription is taken - Handle Ctrl^C in upgrade scripts - Updated moment-timezone +- [TODO DEPLOY] add the `SLOT_DURATION` environment variable (see [doc/environment.md](doc/environment.md#SLOT_DURATION) for configuration details) ## v4.2.3 2019 October 22 diff --git a/app/assets/javascripts/controllers/admin/calendar.js.erb b/app/assets/javascripts/controllers/admin/calendar.js.erb index ee4620858..be492e2a0 100644 --- a/app/assets/javascripts/controllers/admin/calendar.js.erb +++ b/app/assets/javascripts/controllers/admin/calendar.js.erb @@ -30,7 +30,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state const BOOKING_SNAP = '00:30:00'; // We do not allow the creation of slots that are not a multiple of 60 minutes - const SLOT_MULTIPLE = 60; + const SLOT_MULTIPLE = Fablab.slotDuration; /* PUBLIC SCOPE */ @@ -283,12 +283,12 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state return uiCalendarConfig.calendars.calendar.fullCalendar('unselect'); } - // check that the selected slot is an N-hours multiple (ie. not decimal) + // check that the selected slot is an multiple of SLOT_MULTIPLE (ie. not decimal) const slots = Math.trunc((end.valueOf() - start.valueOf()) / (60 * 1000)) / SLOT_MULTIPLE; if (!Number.isInteger(slots)) { - // otherwise, round it to nearest decimal - const nearest = Math.round(slots) * SLOT_MULTIPLE; - end = moment(start).add(nearest, 'minutes'); + // otherwise, round it to upper decimal + const upper = Math.ceil(slots) * SLOT_MULTIPLE; + end = moment(start).add(upper, 'minutes'); } // then we open a modal window to let the admin specify the slot type @@ -507,21 +507,25 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui Tag.query().$promise.then(function (data) { $scope.tags = data; }); - // When we configure a machine availability, do not let the user change the end time, as the total - // time must be dividable by 60 minutes (base slot duration). For training availabilities, the user + // When we configure a machine/space availability, do not let the user change the end time, as the total + // time must be dividable by Fablab.slotDuration minutes (base slot duration). For training availabilities, the user // can configure any duration as it does not matters. $scope.$watch('availability.available_type', function (newValue, oldValue, scope) { if ((newValue === 'machines') || (newValue === 'space')) { $scope.endDateReadOnly = true; - const diff = moment($scope.end).diff($scope.start, 'hours'); // the result is rounded down by moment.js - $scope.end = moment($scope.start).add(diff, 'hours').toDate(); + const slots = Math.trunc(($scope.end.valueOf() - $scope.start.valueOf()) / (60 * 1000)) / Fablab.slotDuration; + if (!Number.isInteger(slots)) { + // otherwise, round it to upper decimal + const upper = Math.ceil(slots) * Fablab.slotDuration; + $scope.end = moment($scope.start).add(upper, 'minutes').toDate(); + } return $scope.availability.end_at = $scope.end; } else { return $scope.endDateReadOnly = false; } }); - // When the start date is changed, if we are configuring a machine availability, + // When the start date is changed, if we are configuring a machine/space availability, // maintain the relative length of the slot (ie. change the end time accordingly) $scope.$watch('start', function (newValue, oldValue, scope) { // for machine or space availabilities, adjust the end time @@ -542,7 +546,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui // Maintain consistency between the end time and the date object in the availability object return $scope.$watch('end', function (newValue, oldValue, scope) { // we prevent the admin from setting the end of the availability before its begining - if (moment($scope.start).add(1, 'hour').isAfter(newValue)) { + if (moment($scope.start).add(Fablab.slotDuration, 'minutes').isAfter(newValue)) { $scope.end = oldValue; } // update availability object diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ea6e2ab0b..88473e6e6 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -7,7 +7,7 @@ module ApplicationHelper require 'message_format' ## machine/spaces availabilities are divided in multiple slots of 60 minutes - SLOT_DURATION ||= 60 + SLOT_DURATION ||= Rails.application.secrets.slot_duration || 60 ## # Verify if the provided attribute is in the provided attributes array, whatever it exists or not diff --git a/app/views/application/index.html.erb b/app/views/application/index.html.erb index 74a5210a0..123ad711b 100644 --- a/app/views/application/index.html.erb +++ b/app/views/application/index.html.erb @@ -20,6 +20,7 @@ Fablab.withoutSpaces = ('<%= Rails.application.secrets.fablab_without_spaces %>' !== 'false'); Fablab.withoutOnlinePayment = ('<%= Rails.application.secrets.fablab_without_online_payments %>' === 'true'); Fablab.withoutInvoices = ('<%= Rails.application.secrets.fablab_without_invoices %>' === 'true'); + Fablab.slotDuration = parseInt("<%= ApplicationHelper::SLOT_DURATION %>", 10); Fablab.disqusShortname = "<%= Rails.application.secrets.disqus_shortname %>"; Fablab.defaultHost = "<%= Rails.application.secrets.default_host %>"; Fablab.gaId = "<%= Rails.application.secrets.google_analytics_id %>"; diff --git a/config/application.yml.default b/config/application.yml.default index 96bbc8238..e8775cb29 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -21,6 +21,8 @@ FABLAB_WITHOUT_SPACES: 'true' FABLAB_WITHOUT_ONLINE_PAYMENT: 'false' FABLAB_WITHOUT_INVOICES: 'false' +SLOT_DURATION: '60' + DEFAULT_MAIL_FROM: Fab Manager Demo # Configure carefully! diff --git a/config/secrets.yml b/config/secrets.yml index d647b149f..87e05bab2 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -20,6 +20,7 @@ development: fablab_without_spaces: <%= ENV["FABLAB_WITHOUT_SPACES"] %> fablab_without_online_payments: <%= ENV["FABLAB_WITHOUT_ONLINE_PAYMENT"] %> fablab_without_invoices: <%= ENV["FABLAB_WITHOUT_INVOICES"] %> + slot_duration: <%= ENV["SLOT_DURATION"] %> default_host: <%= ENV["DEFAULT_HOST"] %> default_protocol: <%= ENV["DEFAULT_PROTOCOL"] %> time_zone: <%= ENV["TIME_ZONE"] %> @@ -62,6 +63,7 @@ test: fablab_without_spaces: false fablab_without_online_payments: false fablab_without_invoices: false + slot_duration: <%= ENV["SLOT_DURATION"] %> default_host: <%= ENV["DEFAULT_HOST"] %> default_protocol: <%= ENV["DEFAULT_PROTOCOL"] %> time_zone: Paris @@ -104,6 +106,7 @@ staging: fablab_without_spaces: <%= ENV["FABLAB_WITHOUT_SPACES"] %> fablab_without_online_payments: <%= ENV["FABLAB_WITHOUT_ONLINE_PAYMENT"] %> fablab_without_invoices: <%= ENV["FABLAB_WITHOUT_INVOICES"] %> + slot_duration: <%= ENV["SLOT_DURATION"] %> default_host: <%= ENV["DEFAULT_HOST"] %> default_protocol: <%= ENV["DEFAULT_PROTOCOL"] %> delivery_method: <%= ENV['DELIVERY_METHOD'] %> @@ -157,6 +160,7 @@ production: fablab_without_spaces: <%= ENV["FABLAB_WITHOUT_SPACES"] %> fablab_without_online_payments: <%= ENV["FABLAB_WITHOUT_ONLINE_PAYMENT"] %> fablab_without_invoices: <%= ENV["FABLAB_WITHOUT_INVOICES"] %> + slot_duration: <%= ENV["SLOT_DURATION"] %> default_host: <%= ENV["DEFAULT_HOST"] %> default_protocol: <%= ENV["DEFAULT_PROTOCOL"] %> delivery_method: <%= ENV['DELIVERY_METHOD'] %> diff --git a/doc/environment.md b/doc/environment.md index ccfd6a922..5ba50d6ec 100644 --- a/doc/environment.md +++ b/doc/environment.md @@ -102,6 +102,15 @@ Valid stripe API keys are still required, even if you don't require online payme If set to 'true', the invoices will be disabled. This is useful if you have your own invoicing system and you want to prevent Fab-manager from generating and sending invoices to members. **Very important**: if you disable invoices, you still have to configure VAT in the interface to prevent errors in accounting and prices. + + + SLOT_DURATION + +Machine and space availabilities are divided in multiple slots of the duration set by this variable. +Default value is 60 minutes (1 hour). + +⚠ Changing this value during the application life may cause serious issues. +Please ensure there's no machine/space availabilities opened to reservation or already reserved **in the future** when you change this value. DEFAULT_MAIL_FROM diff --git a/docker/env.example b/docker/env.example index 8f8333811..5460ef28c 100644 --- a/docker/env.example +++ b/docker/env.example @@ -14,6 +14,8 @@ FABLAB_WITHOUT_SPACES=true FABLAB_WITHOUT_ONLINE_PAYMENT=true FABLAB_WITHOUT_INVOICES=false +SLOT_DURATION=60 + DEFAULT_MAIL_FROM=Fab Manager Demo DEFAULT_HOST=demo.fab-manager.com DEFAULT_PROTOCOL=http