mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
(bug) fix various issues due to slots behavior refactoring
This commit is contained in:
parent
b3795e21ec
commit
d26e2ae313
@ -4,6 +4,7 @@
|
||||
|
||||
- Improved calendars loading time
|
||||
- Refactored and documented the availability-slot-reservation data model
|
||||
- Display bookers names to connected users now apply to all resources
|
||||
- Fix a bug: unable to book a space's slot with an existing reservation
|
||||
- Fix a bug: Unable to import accounts from SSO when the transformation modal was opened but leaved empty
|
||||
|
||||
|
2
Procfile
2
Procfile
@ -1,3 +1,3 @@
|
||||
web: bundle exec rails server puma -p $PORT
|
||||
#web: bundle exec rails server puma -p $PORT
|
||||
worker: bundle exec sidekiq -C ./config/sidekiq.yml
|
||||
webpack: bin/webpacker-dev-server
|
||||
|
@ -73,13 +73,13 @@ class API::AvailabilitiesController < API::ApiController
|
||||
def machine
|
||||
service = Availabilities::AvailabilitiesService.new(current_user)
|
||||
@machine = Machine.friendly.find(params[:machine_id])
|
||||
@slots = service.machines(@machine, @customer, window)
|
||||
@slots = service.machines([@machine], @customer, window)
|
||||
end
|
||||
|
||||
def trainings
|
||||
service = Availabilities::AvailabilitiesService.new(current_user)
|
||||
@trainings = if training_id.is_number? || (training_id.length.positive? && training_id != 'all')
|
||||
[Training.friendly.find(training_id)]
|
||||
@trainings = if params[:training_id].is_number? || (params[:training_id].length.positive? && params[:training_id] != 'all')
|
||||
[Training.friendly.find(params[:training_id])]
|
||||
else
|
||||
Training.all
|
||||
end
|
||||
@ -88,8 +88,8 @@ class API::AvailabilitiesController < API::ApiController
|
||||
|
||||
def spaces
|
||||
service = Availabilities::AvailabilitiesService.new(current_user)
|
||||
@space = Space.friendly.find(space_id)
|
||||
@slots = service.spaces(@space, @customer, window)
|
||||
@space = Space.friendly.find(params[:space_id])
|
||||
@slots = service.spaces([@space], @customer, window)
|
||||
end
|
||||
|
||||
def reservations
|
||||
|
@ -16,8 +16,8 @@
|
||||
* Controller used in the public calendar global
|
||||
*/
|
||||
|
||||
Application.Controllers.controller('CalendarController', ['$scope', '$state', '$aside', 'moment', 'Availability', 'Slot', 'Setting', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', '_t', 'uiCalendarConfig', 'CalendarConfig', 'trainingsPromise', 'machinesPromise', 'spacesPromise', 'iCalendarPromise',
|
||||
function ($scope, $state, $aside, moment, Availability, Slot, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, _t, uiCalendarConfig, CalendarConfig, trainingsPromise, machinesPromise, spacesPromise, iCalendarPromise) {
|
||||
Application.Controllers.controller('CalendarController', ['$scope', '$state', '$aside', 'moment', 'Availability', 'Setting', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', '_t', 'uiCalendarConfig', 'CalendarConfig', 'trainingsPromise', 'machinesPromise', 'spacesPromise', 'iCalendarPromise',
|
||||
function ($scope, $state, $aside, moment, Availability, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, _t, uiCalendarConfig, CalendarConfig, trainingsPromise, machinesPromise, spacesPromise, iCalendarPromise) {
|
||||
/* PRIVATE STATIC CONSTANTS */
|
||||
let currentMachineEvent = null;
|
||||
machinesPromise.forEach(m => m.checked = true);
|
||||
|
@ -493,7 +493,14 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$transi
|
||||
if ($scope.ctrl.member) {
|
||||
Member.get({ id: $scope.ctrl.member.id }, function (member) {
|
||||
$scope.ctrl.member = member;
|
||||
return Availability.spaces({ spaceId: $scope.space.id, member_id: $scope.ctrl.member.id }, function (spaces) {
|
||||
const view = uiCalendarConfig.calendars.calendar.fullCalendar('getView');
|
||||
return Availability.spaces({
|
||||
spaceId: $scope.space.id,
|
||||
member_id: $scope.ctrl.member.id,
|
||||
start: view.start,
|
||||
end: view.end,
|
||||
timezone: Fablab.timezone
|
||||
}, function (spaces) {
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents');
|
||||
return $scope.eventSources.splice(0, 1, {
|
||||
events: spaces,
|
||||
|
@ -282,10 +282,17 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$tra
|
||||
if ($scope.ctrl.member) {
|
||||
Member.get({ id: $scope.ctrl.member.id }, function (member) {
|
||||
$scope.ctrl.member = member;
|
||||
const view = uiCalendarConfig.calendars.calendar.fullCalendar('getView');
|
||||
const id = $transition$.params().id === 'all' ? $transition$.params().id : $scope.training.id;
|
||||
return Availability.trainings({ trainingId: id, member_id: $scope.ctrl.member.id }, function (trainings) {
|
||||
Availability.trainings({
|
||||
trainingId: id,
|
||||
member_id: $scope.ctrl.member.id,
|
||||
start: view.start,
|
||||
end: view.end,
|
||||
timezone: Fablab.timezone
|
||||
}, function (trainings) {
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents');
|
||||
return $scope.eventSources.splice(0, 1, {
|
||||
$scope.eventSources.splice(0, 1, {
|
||||
events: trainings,
|
||||
textColor: 'black'
|
||||
}
|
||||
@ -296,7 +303,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$tra
|
||||
// as the events are re-fetched for the new user, we must re-init the cart
|
||||
$scope.events.reserved = [];
|
||||
$scope.selectedPlan = null;
|
||||
return $scope.plansAreShown = false;
|
||||
$scope.plansAreShown = false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -179,7 +179,7 @@
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row" ng-show="$root.modules.machines">
|
||||
<h3 class="m-l" translate>{{ 'app.admin.settings.display_machine_reservation_user_name' }}</h3>
|
||||
<h3 class="m-l" translate>{{ 'app.admin.settings.display_reservation_user_name' }}</h3>
|
||||
<p class="alert alert-warning m-h-md" ng-bind-html="'app.admin.settings.display_name_info_html' | translate"></p>
|
||||
<boolean-setting name="'display_name_enable'"
|
||||
label="'app.admin.settings.display_name' | translate"
|
||||
|
@ -13,25 +13,29 @@ class Availabilities::AvailabilitiesService
|
||||
@level = level
|
||||
end
|
||||
|
||||
# list all slots for the given machine, with visibility relative to the given user
|
||||
def machines(machine, user, window)
|
||||
availabilities = availabilities(machine.availabilities, 'machines', user, window[:start], window[:end])
|
||||
# list all slots for the given machines, with visibility relative to the given user
|
||||
def machines(machines, user, window)
|
||||
ma_availabilities = Availability.includes('machines_availabilities')
|
||||
.where('machines_availabilities.machine_id': machines.map(&:id))
|
||||
availabilities = availabilities(ma_availabilities, 'machines', user, window[:start], window[:end])
|
||||
|
||||
if @level == 'slot'
|
||||
availabilities.map(&:slots).flatten.map { |s| @service.slot_reserved_status(s, user, [machine]) }
|
||||
availabilities.map(&:slots).flatten.map { |s| @service.slot_reserved_status(s, user, s.availability.machines) }
|
||||
else
|
||||
availabilities.map { |a| @service.availability_reserved_status(a, user, [machine]) }
|
||||
availabilities.map { |a| @service.availability_reserved_status(a, user, a.machines) }
|
||||
end
|
||||
end
|
||||
|
||||
# list all slots for the given space, with visibility relative to the given user
|
||||
def spaces(space, user, window)
|
||||
availabilities = availabilities(space.availabilities, 'space', user, window[:start], window[:end])
|
||||
def spaces(spaces, user, window)
|
||||
sp_availabilities = Availability.includes('spaces_availabilities')
|
||||
.where('spaces_availabilities.space_id': spaces.map(&:id))
|
||||
availabilities = availabilities(sp_availabilities, 'space', user, window[:start], window[:end])
|
||||
|
||||
if @level == 'slot'
|
||||
availabilities.map(&:slots).flatten.map { |s| @service.slot_reserved_status(s, user, [space]) }
|
||||
availabilities.map(&:slots).flatten.map { |s| @service.slot_reserved_status(s, user, s.availability.spaces) }
|
||||
else
|
||||
availabilities.map { |a| @service.availability_reserved_status(a, user, [space]) }
|
||||
availabilities.map { |a| @service.availability_reserved_status(a, user, a.spaces) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -38,10 +38,18 @@ class Availabilities::CreateAvailabilitiesService
|
||||
def create_slots(availability)
|
||||
slot_duration = availability.slot_duration || Setting.get('slot_duration').to_i
|
||||
|
||||
((availability.end_at - availability.start_at) / slot_duration.minutes).to_i.times do |i|
|
||||
if %w[machines space].include?(availability.available_type)
|
||||
((availability.end_at - availability.start_at) / slot_duration.minutes).to_i.times do |i|
|
||||
Slot.new(
|
||||
start_at: availability.start_at + (i * slot_duration).minutes,
|
||||
end_at: availability.start_at + (i * slot_duration).minutes + slot_duration.minutes,
|
||||
availability_id: availability.id
|
||||
).save!
|
||||
end
|
||||
else
|
||||
Slot.new(
|
||||
start_at: availability.start_at + (i * slot_duration).minutes,
|
||||
end_at: availability.start_at + (i * slot_duration).minutes + slot_duration.minutes,
|
||||
start_at: availability.start_at,
|
||||
end_at: availability.end_at,
|
||||
availability_id: availability.id
|
||||
).save!
|
||||
end
|
||||
|
@ -7,36 +7,12 @@ class Availabilities::PublicAvailabilitiesService
|
||||
@service = Availabilities::StatusService.new('public')
|
||||
end
|
||||
|
||||
# provides a list of slots and availabilities for the machines, between the given dates
|
||||
def machines(window, machine_ids, level)
|
||||
machine_ids = [] if machine_ids.nil?
|
||||
service = Availabilities::AvailabilitiesService.new(@current_user, level)
|
||||
slots = []
|
||||
machine_ids.each do |machine_id|
|
||||
machine = Machine.friendly.find(machine_id)
|
||||
slots.concat(service.machines(machine, @current_user, window))
|
||||
end
|
||||
slots
|
||||
end
|
||||
|
||||
# provides a list of slots and availabilities for the spaces, between the given dates
|
||||
def spaces(window, spaces_ids, level)
|
||||
spaces_ids = [] if spaces_ids.nil?
|
||||
service = Availabilities::AvailabilitiesService.new(@current_user, level)
|
||||
slots = []
|
||||
spaces_ids.each do |space_id|
|
||||
space = Space.friendly.find(space_id)
|
||||
slots.concat(service.spaces(space, @current_user, window))
|
||||
end
|
||||
slots
|
||||
end
|
||||
|
||||
def public_availabilities(window, ids, events = false)
|
||||
level = in_same_day(window[:start], window[:end]) ? 'slot' : 'availability'
|
||||
service = Availabilities::AvailabilitiesService.new(@current_user, level)
|
||||
|
||||
machines_slots = machines(window, ids[:machines], level)
|
||||
spaces_slots = spaces(window, ids[:spaces], level)
|
||||
machines_slots = service.machines(Machine.where(id: ids[:machines]), @current_user, window)
|
||||
spaces_slots = service.spaces(Space.where(id:ids[:spaces]), @current_user, window)
|
||||
trainings_slots = service.trainings(Training.where(id: ids[:trainings]), @current_user, window)
|
||||
events_slots = events ? service.events(Event.all, @current_user, window) : []
|
||||
|
||||
|
@ -4,13 +4,13 @@
|
||||
class Availabilities::StatusService
|
||||
def initialize(current_user_role)
|
||||
@current_user_role = current_user_role
|
||||
@show_name = (%w[admin manager].include?(@current_user_role) || Setting.get('display_name_enable'))
|
||||
@show_name = (%w[admin manager].include?(@current_user_role) || (current_user_role && Setting.get('display_name_enable')))
|
||||
end
|
||||
|
||||
# check that the provided slot is reserved for the given reservable (machine, training or space).
|
||||
# Mark it accordingly for display in the calendar
|
||||
def slot_reserved_status(slot, user, reservables)
|
||||
unless reservables.map(&:class).map(&:name).reduce(:==)
|
||||
if reservables.map(&:class).map(&:name).uniq.size > 1
|
||||
raise TypeError('[Availabilities::StatusService#slot_reserved_status] reservables have differents types')
|
||||
end
|
||||
|
||||
@ -24,7 +24,7 @@ class Availabilities::StatusService
|
||||
|
||||
user_slots_reservations = slots_reservations.where('reservations.statistic_profile_id': statistic_profile_id)
|
||||
|
||||
slot.is_reserved = !slots_reservations.empty?
|
||||
slot.is_reserved = !user_slots_reservations.empty?
|
||||
slot.title = slot_title(slots_reservations, user_slots_reservations, reservables)
|
||||
slot.can_modify = true if %w[admin manager].include?(@current_user_role) || !user_slots_reservations.empty?
|
||||
slot.current_user_slots_reservations_ids = user_slots_reservations.map(&:id)
|
||||
@ -34,7 +34,7 @@ class Availabilities::StatusService
|
||||
|
||||
# check that the provided ability is reserved by the given user
|
||||
def availability_reserved_status(availability, user, reservables)
|
||||
unless reservables.map(&:class).map(&:name).reduce(:==)
|
||||
if reservables.map(&:class).map(&:name).uniq.size > 1
|
||||
raise TypeError('[Availabilities::StatusService#availability_reserved_status] reservables have differents types')
|
||||
end
|
||||
|
||||
@ -46,7 +46,7 @@ class Availabilities::StatusService
|
||||
|
||||
user_slots_reservations = slots_reservations.where('reservations.statistic_profile_id': user&.statistic_profile&.id)
|
||||
|
||||
availability.is_reserved = !slots_reservations.empty?
|
||||
availability.is_reserved = !user_slots_reservations.empty?
|
||||
availability.current_user_slots_reservations_ids = user_slots_reservations.map(&:id)
|
||||
availability
|
||||
end
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
json.array!(@slots) do |slot|
|
||||
json.partial! 'api/availabilities/slot', slot: slot, operator_role: @operator_role
|
||||
json.borderColor trainings_events_border_color(slot)
|
||||
json.borderColor trainings_events_border_color(slot.availability)
|
||||
|
||||
json.is_completed slot.full?
|
||||
json.nb_total_places slot.nb_total_places
|
||||
json.nb_total_places slot.availability.nb_total_places
|
||||
|
||||
json.training do
|
||||
json.id slot.availability.trainings.first.id
|
||||
|
@ -1373,8 +1373,8 @@ en:
|
||||
visibility_yearly: "maximum visibility for annual subscribers"
|
||||
visibility_others: "maximum visibility for other members"
|
||||
display: "Display"
|
||||
display_name_info_html: "When enabled, members and visitors browsing the calendar or booking a machine will see the name of the members who has booked some slots. When disabled, only administrators and managers will view the names.<br/><strong>Warning:</strong> if you enable this feature, remember to write it in your privacy policy."
|
||||
display_machine_reservation_user_name: "Display the full name of the user who booked a machine slot"
|
||||
display_name_info_html: "When enabled, connected members browsing the calendar or booking a resource will see the name of the members who has already booked some slots. When disabled, only administrators and managers will view the names.<br/><strong>Warning:</strong> if you enable this feature, please write it down in your privacy policy."
|
||||
display_reservation_user_name: "Display the full name of the user(s) who booked a slots"
|
||||
display_name: "Display the name"
|
||||
display_name_enable: "name display"
|
||||
events_in_the_calendar: "Display the events in the calendar"
|
||||
|
@ -13,28 +13,28 @@ class AvailabilitiesServiceTest < ActiveSupport::TestCase
|
||||
|
||||
test 'no machines availabilities during given window' do
|
||||
service = Availabilities::AvailabilitiesService.new(@no_subscription)
|
||||
slots = service.machines(Machine.find(3), @no_subscription, { start: DateTime.current.beginning_of_day, end: 1.day.from_now.end_of_day })
|
||||
slots = service.machines([Machine.find(3)], @no_subscription, { start: DateTime.current.beginning_of_day, end: 1.day.from_now.end_of_day })
|
||||
|
||||
assert_empty slots
|
||||
end
|
||||
|
||||
test 'no machines availabilities for user tags' do
|
||||
service = Availabilities::AvailabilitiesService.new(@no_subscription)
|
||||
slots = service.machines(Machine.find(3), @no_subscription, { start: 2.days.from_now.beginning_of_day, end: 4.days.from_now.end_of_day })
|
||||
slots = service.machines([Machine.find(3)], @no_subscription, { start: 2.days.from_now.beginning_of_day, end: 4.days.from_now.end_of_day })
|
||||
|
||||
assert_empty slots
|
||||
end
|
||||
|
||||
test 'no past availabilities for members' do
|
||||
service = Availabilities::AvailabilitiesService.new(@no_subscription)
|
||||
slots = service.machines(Machine.find(2), @no_subscription, { start: DateTime.parse('2015-06-15').beginning_of_day, end: DateTime.parse('2015-06-15').end_of_day })
|
||||
slots = service.machines([Machine.find(2)], @no_subscription, { start: DateTime.parse('2015-06-15').beginning_of_day, end: DateTime.parse('2015-06-15').end_of_day })
|
||||
|
||||
assert_empty slots
|
||||
end
|
||||
|
||||
test 'admin cannot see past availabilities further than 1 month' do
|
||||
service = Availabilities::AvailabilitiesService.new(@admin)
|
||||
slots = service.machines(Machine.find(2), @no_subscription, { start: DateTime.parse('2015-06-15').beginning_of_day, end: DateTime.parse('2015-06-15').end_of_day })
|
||||
slots = service.machines([Machine.find(2)], @no_subscription, { start: DateTime.parse('2015-06-15').beginning_of_day, end: DateTime.parse('2015-06-15').end_of_day })
|
||||
|
||||
assert_empty slots
|
||||
end
|
||||
@ -51,7 +51,7 @@ class AvailabilitiesServiceTest < ActiveSupport::TestCase
|
||||
|
||||
test 'machines availabilities' do
|
||||
service = Availabilities::AvailabilitiesService.new(@no_subscription)
|
||||
slots = service.machines(Machine.find(1), @no_subscription, { start: 2.days.from_now.beginning_of_day, end: 4.days.from_now.end_of_day })
|
||||
slots = service.machines([Machine.find(1)], @no_subscription, { start: 2.days.from_now.beginning_of_day, end: 4.days.from_now.end_of_day })
|
||||
|
||||
assert_not_empty slots
|
||||
assert_equal Availability.find(7).slots.count, slots.count
|
||||
@ -61,7 +61,7 @@ class AvailabilitiesServiceTest < ActiveSupport::TestCase
|
||||
|
||||
test 'spaces availabilities' do
|
||||
service = Availabilities::AvailabilitiesService.new(@no_subscription)
|
||||
slots = service.spaces(Space.find(1), @no_subscription, { start: 2.days.from_now.beginning_of_day, end: 4.days.from_now.end_of_day })
|
||||
slots = service.spaces([Space.find(1)], @no_subscription, { start: 2.days.from_now.beginning_of_day, end: 4.days.from_now.end_of_day })
|
||||
|
||||
assert_not_empty slots
|
||||
assert_equal Availability.find(18).slots.count, slots.count
|
||||
|
Loading…
x
Reference in New Issue
Block a user