mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-29 10:24:20 +01:00
[feature] ability to block new reservations (#78)
On a machine, space or training availability slot, if there's no current reservation or if all reservations were cancelled, the slot can be locked to prevent any new reservations on it. Once locked it will disapear from the members calendars. The admins can also to unlock these slots later.
This commit is contained in:
commit
f4ddd92b40
@ -132,6 +132,40 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Mark the selected slot as unavailable for new reservations or allow reservations again on it
|
||||||
|
##
|
||||||
|
$scope.toggleLockReservations = ->
|
||||||
|
# first, define a shortcut to the lock property
|
||||||
|
locked = $scope.availability.lock
|
||||||
|
# then check if we'll allow reservations locking
|
||||||
|
prevent = !locked # if currently locked, allow unlock anyway
|
||||||
|
if (!locked)
|
||||||
|
prevent = false
|
||||||
|
angular.forEach $scope.reservations, (r) ->
|
||||||
|
if r.canceled_at == null
|
||||||
|
prevent = true # if currently unlocked and has any non-cancelled reservation, disallow locking
|
||||||
|
if (!prevent)
|
||||||
|
# open a confirmation dialog
|
||||||
|
dialogs.confirm
|
||||||
|
resolve:
|
||||||
|
object: ->
|
||||||
|
title: _t('admin_calendar.confirmation_required')
|
||||||
|
msg: if locked then _t("admin_calendar.do_you_really_want_to_allow_reservations") else _t("admin_calendar.do_you_really_want_to_block_this_slot")
|
||||||
|
, ->
|
||||||
|
# the admin has confirmed, lock/unlock the slot
|
||||||
|
Availability.lock {id: $scope.availability.id}, {lock: !locked}
|
||||||
|
, (data) -> # success
|
||||||
|
$scope.availability = data
|
||||||
|
growl.success(if locked then _t('admin_calendar.unlocking_success') else _t('admin_calendar.locking_success') )
|
||||||
|
uiCalendarConfig.calendars.calendar.fullCalendar 'refetchEvents'
|
||||||
|
, (error) -> # failed
|
||||||
|
growl.error(if locked then _t('admin_calendar.unlocking_failed') else _t('admin_calendar.locking_failed'))
|
||||||
|
else
|
||||||
|
growl.error(_t('admin_calendar.unlockable_because_reservations'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### PRIVATE SCOPE ###
|
### PRIVATE SCOPE ###
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -256,6 +256,9 @@ Application.Controllers.controller "ShowMachineController", ['$scope', '$state',
|
|||||||
$state.go('app.public.machines_list')
|
$state.go('app.public.machines_list')
|
||||||
, (error)->
|
, (error)->
|
||||||
growl.warning(_t('the_machine_cant_be_deleted_because_it_is_already_reserved_by_some_users'))
|
growl.warning(_t('the_machine_cant_be_deleted_because_it_is_already_reserved_by_some_users'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Callback to book a reservation for the current machine
|
# Callback to book a reservation for the current machine
|
||||||
##
|
##
|
||||||
@ -412,6 +415,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", '$stat
|
|||||||
updateCalendar()
|
updateCalendar()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# When modifying an already booked reservation, callback when the modification was successfully done.
|
# When modifying an already booked reservation, callback when the modification was successfully done.
|
||||||
##
|
##
|
||||||
|
@ -24,4 +24,7 @@ Application.Services.factory 'Availability', ["$resource", ($resource)->
|
|||||||
isArray: true
|
isArray: true
|
||||||
update:
|
update:
|
||||||
method: 'PUT'
|
method: 'PUT'
|
||||||
|
lock:
|
||||||
|
method: 'PUT'
|
||||||
|
url: '/api/availabilities/:id/lock'
|
||||||
]
|
]
|
||||||
|
@ -360,6 +360,10 @@ body.container{
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.reservations-locked {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
.reservation-canceled {
|
.reservation-canceled {
|
||||||
color: #606060;
|
color: #606060;
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
|
@ -42,11 +42,12 @@
|
|||||||
</a>
|
</a>
|
||||||
<iframe name="export-frame" height="0" width="0" class="none"></iframe>
|
<iframe name="export-frame" height="0" width="0" class="none"></iframe>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget panel b-a m m-t-lg">
|
|
||||||
|
<div class="widget panel b-a m m-t-lg" ng-if="availability">
|
||||||
<div class="panel-heading b-b small">
|
<div class="panel-heading b-b small">
|
||||||
<h3 translate>{{ 'admin_calendar.ongoing_reservations' }}</h3>
|
<h3 translate>{{ 'admin_calendar.ongoing_reservations' }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-content no-bg auto wrapper">
|
<div class="widget-content no-bg auto wrapper" ng-class="{'reservations-locked': availability.lock}">
|
||||||
<ul class="list-unstyled" ng-if="reservations.length > 0">
|
<ul class="list-unstyled" ng-if="reservations.length > 0">
|
||||||
<li ng-repeat="r in reservations" class="m-b-xs" ng-class="{'reservation-canceled':r.canceled_at}">
|
<li ng-repeat="r in reservations" class="m-b-xs" ng-class="{'reservation-canceled':r.canceled_at}">
|
||||||
{{r.user.name}}
|
{{r.user.name}}
|
||||||
@ -55,25 +56,42 @@
|
|||||||
<span class="btn btn-warning btn-xs" ng-click="cancelBooking(r)" ng-if="!r.canceled_at"><i class="fa fa-times red"></i></span>
|
<span class="btn btn-warning btn-xs" ng-click="cancelBooking(r)" ng-if="!r.canceled_at"><i class="fa fa-times red"></i></span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div ng-if="reservations.length == 0" translate>{{ 'admin_calendar.no_reservations' }}</div>
|
<div ng-show="reservations.length == 0" translate>{{ 'admin_calendar.no_reservations' }}</div>
|
||||||
|
<div class="m-t" ng-show="availability.lock"><i class="fa fa-ban"/> <span class="m-l-xs" translate>{{ 'admin_calendar.reservations_locked' }}</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="widget panel b-a m m-t-lg" ng-if="availability.machine_ids.length > 0">
|
||||||
|
<div class="panel-heading b-b small">
|
||||||
|
<h3 translate>{{ 'admin_calendar.machines' }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="widget-content no-bg auto wrapper">
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li ng-repeat="m in machines" class="m-b-xs" ng-show="availability.machine_ids.indexOf(m.id) > -1">
|
||||||
|
{{m.name}}
|
||||||
|
<span class="btn btn-warning btn-xs" ng-click="removeMachine(m)" ng-if="availability.machine_ids.length > 1"><i class="fa fa-times red"></i></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="widget panel b-a m m-t-lg" ng-if="availability">
|
||||||
|
<div class="panel-heading b-b small">
|
||||||
|
<h3 translate>{{ 'admin_calendar.actions' }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="widget-content no-bg auto wrapper">
|
||||||
|
<button class="btn btn-default" ng-click="toggleLockReservations()">
|
||||||
|
<span ng-hide="availability.lock">
|
||||||
|
<i class="fa fa-stop" />
|
||||||
|
<span class="m-l-xs" translate>{{ 'admin_calendar.block_reservations' }}</span>
|
||||||
|
</span>
|
||||||
|
<span ng-show="availability.lock">
|
||||||
|
<i class="fa fa-play" />
|
||||||
|
<span class="m-l-xs" translate>{{ 'admin_calendar.allow_reservations' }}</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-12 col-lg-3" ng-if="availability.machine_ids.length > 0">
|
</section>
|
||||||
<div class="widget panel b-a m m-t-lg">
|
|
||||||
<div class="panel-heading b-b small">
|
|
||||||
<h3 translate>{{ 'admin_calendar.machines' }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="widget-content no-bg auto wrapper">
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li ng-repeat="m in machines" class="m-b-xs" ng-show="availability.machine_ids.indexOf(m.id) > -1">
|
|
||||||
{{m.name}}
|
|
||||||
<span class="btn btn-warning btn-xs" ng-click="removeMachine(m)" ng-if="availability.machine_ids.length > 1"><i class="fa fa-times red"></i></span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
@ -2,7 +2,7 @@ class API::AvailabilitiesController < API::ApiController
|
|||||||
include FablabConfiguration
|
include FablabConfiguration
|
||||||
|
|
||||||
before_action :authenticate_user!, except: [:public]
|
before_action :authenticate_user!, except: [:public]
|
||||||
before_action :set_availability, only: [:show, :update, :destroy, :reservations]
|
before_action :set_availability, only: [:show, :update, :destroy, :reservations, :lock]
|
||||||
before_action :define_max_visibility, only: [:machine, :trainings, :spaces]
|
before_action :define_max_visibility, only: [:machine, :trainings, :spaces]
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
@ -21,22 +21,32 @@ class API::AvailabilitiesController < API::ApiController
|
|||||||
def public
|
def public
|
||||||
start_date = ActiveSupport::TimeZone[params[:timezone]].parse(params[:start])
|
start_date = ActiveSupport::TimeZone[params[:timezone]].parse(params[:start])
|
||||||
end_date = ActiveSupport::TimeZone[params[:timezone]].parse(params[:end]).end_of_day
|
end_date = ActiveSupport::TimeZone[params[:timezone]].parse(params[:end]).end_of_day
|
||||||
@reservations = Reservation.includes(:slots, user: [:profile]).references(:slots, :user).where('slots.start_at >= ? AND slots.end_at <= ?', start_date, end_date)
|
@reservations = Reservation.includes(:slots, user: [:profile]).references(:slots, :user)
|
||||||
|
.where('slots.start_at >= ? AND slots.end_at <= ?', start_date, end_date)
|
||||||
|
|
||||||
# request for 1 single day
|
# request for 1 single day
|
||||||
if in_same_day(start_date, end_date)
|
if in_same_day(start_date, end_date)
|
||||||
# trainings, events
|
# trainings, events
|
||||||
@training_and_event_availabilities = Availability.includes(:tags, :trainings, :event, :slots).where(available_type: %w(training event))
|
@training_and_event_availabilities = Availability.includes(:tags, :trainings, :event, :slots).where(available_type: %w(training event))
|
||||||
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
||||||
|
.where(lock: false)
|
||||||
# machines
|
# machines
|
||||||
@machine_availabilities = Availability.includes(:tags, :machines).where(available_type: 'machines')
|
@machine_availabilities = Availability.includes(:tags, :machines).where(available_type: 'machines')
|
||||||
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
||||||
|
.where(lock: false)
|
||||||
@machine_slots = []
|
@machine_slots = []
|
||||||
@machine_availabilities.each do |a|
|
@machine_availabilities.each do |a|
|
||||||
a.machines.each do |machine|
|
a.machines.each do |machine|
|
||||||
if params[:m] and params[:m].include?(machine.id.to_s)
|
if params[:m] and params[:m].include?(machine.id.to_s)
|
||||||
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
|
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
|
||||||
slot = Slot.new(start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes, end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes, availability_id: a.id, availability: a, machine: machine, title: machine.name)
|
slot = Slot.new(
|
||||||
|
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
|
||||||
|
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
|
||||||
|
availability_id: a.id,
|
||||||
|
availability: a,
|
||||||
|
machine: machine,
|
||||||
|
title: machine.name
|
||||||
|
)
|
||||||
slot = verify_machine_is_reserved(slot, @reservations, current_user, '')
|
slot = verify_machine_is_reserved(slot, @reservations, current_user, '')
|
||||||
@machine_slots << slot
|
@machine_slots << slot
|
||||||
end
|
end
|
||||||
@ -47,6 +57,7 @@ class API::AvailabilitiesController < API::ApiController
|
|||||||
# spaces
|
# spaces
|
||||||
@space_availabilities = Availability.includes(:tags, :spaces).where(available_type: 'space')
|
@space_availabilities = Availability.includes(:tags, :spaces).where(available_type: 'space')
|
||||||
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
||||||
|
.where(lock: false)
|
||||||
|
|
||||||
if params[:s]
|
if params[:s]
|
||||||
@space_availabilities.where(available_id: params[:s])
|
@space_availabilities.where(available_id: params[:s])
|
||||||
@ -57,7 +68,14 @@ class API::AvailabilitiesController < API::ApiController
|
|||||||
space = a.spaces.first
|
space = a.spaces.first
|
||||||
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
|
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
|
||||||
if (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
|
if (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
|
||||||
slot = Slot.new(start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes, end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes, availability_id: a.id, availability: a, space: space, title: space.name)
|
slot = Slot.new(
|
||||||
|
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
|
||||||
|
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
|
||||||
|
availability_id: a.id,
|
||||||
|
availability: a,
|
||||||
|
space: space,
|
||||||
|
title: space.name
|
||||||
|
)
|
||||||
slot = verify_space_is_reserved(slot, @reservations, current_user, '')
|
slot = verify_space_is_reserved(slot, @reservations, current_user, '')
|
||||||
@space_slots << slot
|
@space_slots << slot
|
||||||
end
|
end
|
||||||
@ -69,6 +87,7 @@ class API::AvailabilitiesController < API::ApiController
|
|||||||
else
|
else
|
||||||
@availabilities = Availability.includes(:tags, :machines, :trainings, :spaces, :event, :slots)
|
@availabilities = Availability.includes(:tags, :machines, :trainings, :spaces, :event, :slots)
|
||||||
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
||||||
|
.where(lock: false)
|
||||||
@availabilities.each do |a|
|
@availabilities.each do |a|
|
||||||
if a.available_type == 'training' or a.available_type == 'event'
|
if a.available_type == 'training' or a.available_type == 'event'
|
||||||
a = verify_training_event_is_reserved(a, @reservations, current_user)
|
a = verify_training_event_is_reserved(a, @reservations, current_user)
|
||||||
@ -125,16 +144,27 @@ class API::AvailabilitiesController < API::ApiController
|
|||||||
@slots = []
|
@slots = []
|
||||||
@reservations = Reservation.where('reservable_type = ? and reservable_id = ?', @machine.class.to_s, @machine.id).includes(:slots, user: [:profile]).references(:slots, :user).where('slots.start_at > ?', Time.now)
|
@reservations = Reservation.where('reservable_type = ? and reservable_id = ?', @machine.class.to_s, @machine.id).includes(:slots, user: [:profile]).references(:slots, :user).where('slots.start_at > ?', Time.now)
|
||||||
if @user.is_admin?
|
if @user.is_admin?
|
||||||
@availabilities = @machine.availabilities.includes(:tags).where("end_at > ? AND available_type = 'machines'", Time.now)
|
@availabilities = @machine.availabilities.includes(:tags)
|
||||||
|
.where("end_at > ? AND available_type = 'machines'", Time.now)
|
||||||
|
.where(lock: false)
|
||||||
else
|
else
|
||||||
end_at = @visi_max_other
|
end_at = @visi_max_other
|
||||||
end_at = @visi_max_year if is_subscription_year(@user)
|
end_at = @visi_max_year if is_subscription_year(@user)
|
||||||
@availabilities = @machine.availabilities.includes(:tags).where("end_at > ? AND end_at < ? AND available_type = 'machines'", Time.now, end_at).where('availability_tags.tag_id' => @user.tag_ids.concat([nil]))
|
@availabilities = @machine.availabilities.includes(:tags).where("end_at > ? AND end_at < ? AND available_type = 'machines'", Time.now, end_at)
|
||||||
|
.where('availability_tags.tag_id' => @user.tag_ids.concat([nil]))
|
||||||
|
.where(lock: false)
|
||||||
end
|
end
|
||||||
@availabilities.each do |a|
|
@availabilities.each do |a|
|
||||||
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
|
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
|
||||||
if (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
|
if (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
|
||||||
slot = Slot.new(start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes, end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes, availability_id: a.id, availability: a, machine: @machine, title: '')
|
slot = Slot.new(
|
||||||
|
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
|
||||||
|
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
|
||||||
|
availability_id: a.id,
|
||||||
|
availability: a,
|
||||||
|
machine: @machine,
|
||||||
|
title: ''
|
||||||
|
)
|
||||||
slot = verify_machine_is_reserved(slot, @reservations, current_user, @current_user_role)
|
slot = verify_machine_is_reserved(slot, @reservations, current_user, @current_user_role)
|
||||||
@slots << slot
|
@slots << slot
|
||||||
end
|
end
|
||||||
@ -167,12 +197,17 @@ class API::AvailabilitiesController < API::ApiController
|
|||||||
# who made the request?
|
# who made the request?
|
||||||
# 1) an admin (he can see all future availabilities)
|
# 1) an admin (he can see all future availabilities)
|
||||||
if current_user.is_admin?
|
if current_user.is_admin?
|
||||||
@availabilities = @availabilities.includes(:tags, :slots, trainings: [:machines]).where('availabilities.start_at > ?', Time.now)
|
@availabilities = @availabilities.includes(:tags, :slots, trainings: [:machines])
|
||||||
|
.where('availabilities.start_at > ?', Time.now)
|
||||||
|
.where(lock: false)
|
||||||
# 2) an user (he cannot see availabilities further than 1 (or 3) months)
|
# 2) an user (he cannot see availabilities further than 1 (or 3) months)
|
||||||
else
|
else
|
||||||
end_at = @visi_max_year
|
end_at = @visi_max_year
|
||||||
end_at = @visi_max_year if can_show_slot_plus_three_months(@user)
|
end_at = @visi_max_year if can_show_slot_plus_three_months(@user)
|
||||||
@availabilities = @availabilities.includes(:tags, :slots, :availability_tags, trainings: [:machines]).where('availabilities.start_at > ? AND availabilities.start_at < ?', Time.now, end_at).where('availability_tags.tag_id' => @user.tag_ids.concat([nil]))
|
@availabilities = @availabilities.includes(:tags, :slots, :availability_tags, trainings: [:machines])
|
||||||
|
.where('availabilities.start_at > ? AND availabilities.start_at < ?', Time.now, end_at)
|
||||||
|
.where('availability_tags.tag_id' => @user.tag_ids.concat([nil]))
|
||||||
|
.where(lock: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
# finally, we merge the availabilities with the reservations
|
# finally, we merge the availabilities with the reservations
|
||||||
@ -190,18 +225,32 @@ class API::AvailabilitiesController < API::ApiController
|
|||||||
@current_user_role = current_user.is_admin? ? 'admin' : 'user'
|
@current_user_role = current_user.is_admin? ? 'admin' : 'user'
|
||||||
@space = Space.friendly.find(params[:space_id])
|
@space = Space.friendly.find(params[:space_id])
|
||||||
@slots = []
|
@slots = []
|
||||||
@reservations = Reservation.where('reservable_type = ? and reservable_id = ?', @space.class.to_s, @space.id).includes(:slots, user: [:profile]).references(:slots, :user).where('slots.start_at > ?', Time.now)
|
@reservations = Reservation.where('reservable_type = ? and reservable_id = ?', @space.class.to_s, @space.id)
|
||||||
|
.includes(:slots, user: [:profile]).references(:slots, :user)
|
||||||
|
.where('slots.start_at > ?', Time.now)
|
||||||
if current_user.is_admin?
|
if current_user.is_admin?
|
||||||
@availabilities = @space.availabilities.includes(:tags).where("end_at > ? AND available_type = 'space'", Time.now)
|
@availabilities = @space.availabilities.includes(:tags)
|
||||||
|
.where("end_at > ? AND available_type = 'space'", Time.now)
|
||||||
|
.where(lock: false)
|
||||||
else
|
else
|
||||||
end_at = @visi_max_other
|
end_at = @visi_max_other
|
||||||
end_at = @visi_max_year if is_subscription_year(@user)
|
end_at = @visi_max_year if is_subscription_year(@user)
|
||||||
@availabilities = @space.availabilities.includes(:tags).where("end_at > ? AND end_at < ? AND available_type = 'space'", Time.now, end_at).where('availability_tags.tag_id' => @user.tag_ids.concat([nil]))
|
@availabilities = @space.availabilities.includes(:tags)
|
||||||
|
.where("end_at > ? AND end_at < ? AND available_type = 'space'", Time.now, end_at)
|
||||||
|
.where('availability_tags.tag_id' => @user.tag_ids.concat([nil]))
|
||||||
|
.where(lock: false)
|
||||||
end
|
end
|
||||||
@availabilities.each do |a|
|
@availabilities.each do |a|
|
||||||
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
|
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
|
||||||
if (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
|
if (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
|
||||||
slot = Slot.new(start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes, end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes, availability_id: a.id, availability: a, space: @space, title: '')
|
slot = Slot.new(
|
||||||
|
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
|
||||||
|
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
|
||||||
|
availability_id: a.id,
|
||||||
|
availability: a,
|
||||||
|
space: @space,
|
||||||
|
title: ''
|
||||||
|
)
|
||||||
slot = verify_space_is_reserved(slot, @reservations, @user, @current_user_role)
|
slot = verify_space_is_reserved(slot, @reservations, @user, @current_user_role)
|
||||||
@slots << slot
|
@slots << slot
|
||||||
end
|
end
|
||||||
@ -235,6 +284,15 @@ class API::AvailabilitiesController < API::ApiController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def lock
|
||||||
|
authorize @availability
|
||||||
|
if @availability.update_attributes(lock: lock_params)
|
||||||
|
render :show, status: :ok, location: @availability
|
||||||
|
else
|
||||||
|
render json: @availability.errors, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def set_availability
|
def set_availability
|
||||||
@availability = Availability.find(params[:id])
|
@availability = Availability.find(params[:id])
|
||||||
@ -245,6 +303,10 @@ class API::AvailabilitiesController < API::ApiController
|
|||||||
:machines_attributes => [:id, :_destroy])
|
:machines_attributes => [:id, :_destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def lock_params
|
||||||
|
params.require(:lock)
|
||||||
|
end
|
||||||
|
|
||||||
def is_reserved_availability(availability, user_id)
|
def is_reserved_availability(availability, user_id)
|
||||||
reserved_slots = []
|
reserved_slots = []
|
||||||
availability.slots.each do |s|
|
availability.slots.each do |s|
|
||||||
|
3
app/exceptions/invoice_total_different_error.rb
Normal file
3
app/exceptions/invoice_total_different_error.rb
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Raised when total of reservation isn't equal to the total of stripe's invoice
|
||||||
|
class InvoiceTotalDifferentError < StandardError
|
||||||
|
end
|
@ -1,3 +0,0 @@
|
|||||||
# Raised when total of reservation isnt equal total of strip's invoice
|
|
||||||
class InvoiceTotalDiffrentError < StandardError
|
|
||||||
end
|
|
3
app/exceptions/locked_error.rb
Normal file
3
app/exceptions/locked_error.rb
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Raised when reserving on a locked availability
|
||||||
|
class LockedError < StandardError
|
||||||
|
end
|
@ -47,6 +47,13 @@ class Reservation < ActiveRecord::Base
|
|||||||
plan = nil
|
plan = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# check that none of the reserved availabilities was locked
|
||||||
|
slots.each do |slot|
|
||||||
|
if slot.availability.lock
|
||||||
|
raise LockedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
case reservable
|
case reservable
|
||||||
|
|
||||||
@ -260,7 +267,7 @@ class Reservation < ActiveRecord::Base
|
|||||||
# this function only check reservation total is equal strip invoice total when
|
# this function only check reservation total is equal strip invoice total when
|
||||||
# pay only reservation not reservation + subscription
|
# pay only reservation not reservation + subscription
|
||||||
#if !is_equal_reservation_total_and_stp_invoice_total(stp_invoice, coupon_code)
|
#if !is_equal_reservation_total_and_stp_invoice_total(stp_invoice, coupon_code)
|
||||||
#raise InvoiceTotalDiffrentError
|
#raise InvoiceTotalDifferentError
|
||||||
#end
|
#end
|
||||||
stp_invoice.pay
|
stp_invoice.pay
|
||||||
card.delete if card
|
card.delete if card
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
class AvailabilityPolicy < ApplicationPolicy
|
class AvailabilityPolicy < ApplicationPolicy
|
||||||
%w(index? show? create? update? destroy? reservations? export?).each do |action|
|
%w(index? show? create? update? destroy? reservations? export? lock?).each do |action|
|
||||||
define_method action do
|
define_method action do
|
||||||
user.is_admin?
|
user.is_admin?
|
||||||
end
|
end
|
||||||
|
@ -6,11 +6,12 @@ json.array!(@availabilities) do |availability|
|
|||||||
json.available_type availability.available_type
|
json.available_type availability.available_type
|
||||||
json.machine_ids availability.machine_ids
|
json.machine_ids availability.machine_ids
|
||||||
json.training_ids availability.training_ids
|
json.training_ids availability.training_ids
|
||||||
json.backgroundColor 'white'
|
json.backgroundColor !availability.lock ? 'white' : '#f5f5f5'
|
||||||
json.borderColor availability_border_color(availability)
|
json.borderColor availability_border_color(availability)
|
||||||
json.tag_ids availability.tag_ids
|
json.tag_ids availability.tag_ids
|
||||||
json.tags availability.tags do |t|
|
json.tags availability.tags do |t|
|
||||||
json.id t.id
|
json.id t.id
|
||||||
json.name t.name
|
json.name t.name
|
||||||
end
|
end
|
||||||
|
json.lock availability.lock
|
||||||
end
|
end
|
||||||
|
@ -11,3 +11,4 @@ json.tags @availability.tags do |t|
|
|||||||
json.id t.id
|
json.id t.id
|
||||||
json.name t.name
|
json.name t.name
|
||||||
end
|
end
|
||||||
|
json.lock @availability.lock
|
||||||
|
@ -48,6 +48,17 @@ en:
|
|||||||
unable_to_delete_the_slot_START-END_because_it_s_already_reserved_by_a_member: "Unable to delete the slot {{START}} - {{END}} because it's already reserved by a member" # angular interpolation
|
unable_to_delete_the_slot_START-END_because_it_s_already_reserved_by_a_member: "Unable to delete the slot {{START}} - {{END}} because it's already reserved by a member" # angular interpolation
|
||||||
you_should_select_at_least_a_machine: "You should select at least one machine on this slot."
|
you_should_select_at_least_a_machine: "You should select at least one machine on this slot."
|
||||||
export_is_running_you_ll_be_notified_when_its_ready: "Export is running. You'll be notified when it's ready."
|
export_is_running_you_ll_be_notified_when_its_ready: "Export is running. You'll be notified when it's ready."
|
||||||
|
actions: "Actions"
|
||||||
|
block_reservations: "Block reservations"
|
||||||
|
do_you_really_want_to_block_this_slot: "Do you really want to block new reservations on this slot? It will become invisible to users."
|
||||||
|
locking_success: "Slot successfully locked, it won't appear any longer in the user calendar"
|
||||||
|
locking_failed: "An error occurred. Slot locking has failed"
|
||||||
|
allow_reservations: "Allow reservations"
|
||||||
|
do_you_really_want_to_allow_reservations: "Do you really want to allow booking again on this slot? It will become visible for the users."
|
||||||
|
unlocking_success: "Slot successfully unlocked, it will appear again in the user calendar"
|
||||||
|
unlocking_failed: "An error occurred. Slot unlocking has failed"
|
||||||
|
reservations_locked: "Booking is blocked"
|
||||||
|
unlockable_because_reservations: "Unable to block booking on this slot because some uncancelled reservations exist on it."
|
||||||
|
|
||||||
project_elements:
|
project_elements:
|
||||||
# management of the projects' components
|
# management of the projects' components
|
||||||
|
@ -48,6 +48,17 @@ fr:
|
|||||||
unable_to_delete_the_slot_START-END_because_it_s_already_reserved_by_a_member: "Le créneau {{START}} - {{END}} n'a pu être supprimé car il est déjà réservé par un membre" # angular interpolation
|
unable_to_delete_the_slot_START-END_because_it_s_already_reserved_by_a_member: "Le créneau {{START}} - {{END}} n'a pu être supprimé car il est déjà réservé par un membre" # angular interpolation
|
||||||
you_should_select_at_least_a_machine: "Vous devriez sélectionne au moins une machine pour ce créneau."
|
you_should_select_at_least_a_machine: "Vous devriez sélectionne au moins une machine pour ce créneau."
|
||||||
export_is_running_you_ll_be_notified_when_its_ready: "L'export est en cours. Vous serez notifié lorsqu'il sera prêt."
|
export_is_running_you_ll_be_notified_when_its_ready: "L'export est en cours. Vous serez notifié lorsqu'il sera prêt."
|
||||||
|
actions: "Actions"
|
||||||
|
block_reservations: "Bloquer les réservations"
|
||||||
|
do_you_really_want_to_block_this_slot: "Êtes vous sur de vouloir bloquer les nouvelles réservations sur ce créneau ? Il deviendra alors invisible pour les utilisateurs."
|
||||||
|
locking_success: "Le créneau a bien été verrouillé, il n'apparaitra plus dans le calendrier utilisateur"
|
||||||
|
locking_failed: "Une erreur est survenue. Le verrouillage du créneau a échoué"
|
||||||
|
allow_reservations: "Autoriser les réservations"
|
||||||
|
do_you_really_want_to_allow_reservations: "Êtes vous sur de vouloir autoriser de nouveau la prise de réservations sur ce créneau ? Il deviendra alors visible pour les utilisateurs."
|
||||||
|
unlocking_success: "Le créneau a bien été déverrouillé, il apparaîtra de nouveau dans le calendrier utilisateur"
|
||||||
|
unlocking_failed: "Une erreur est survenue. Le déverrouillage du créneau a échoué"
|
||||||
|
reservations_locked: "Réservations bloquées"
|
||||||
|
unlockable_because_reservations: "Impossible de bloquer les réservations sur ce créneau car il existe des réservations non annulées sur celui-ci."
|
||||||
|
|
||||||
project_elements:
|
project_elements:
|
||||||
# gestion des éléments constituant les projets
|
# gestion des éléments constituant les projets
|
||||||
|
@ -48,6 +48,17 @@ pt:
|
|||||||
unable_to_delete_the_slot_START-END_because_it_s_already_reserved_by_a_member: "Não é possível deletar o slot {{START}} - {{END}} porque já foi reservado por um membro" # angular interpolation
|
unable_to_delete_the_slot_START-END_because_it_s_already_reserved_by_a_member: "Não é possível deletar o slot {{START}} - {{END}} porque já foi reservado por um membro" # angular interpolation
|
||||||
you_should_select_at_least_a_machine: "Você deveria selecionar a última máquina neste slot."
|
you_should_select_at_least_a_machine: "Você deveria selecionar a última máquina neste slot."
|
||||||
export_is_running_you_ll_be_notified_when_its_ready: "A Exportação está em andamento. Você será notificado quando terminar."
|
export_is_running_you_ll_be_notified_when_its_ready: "A Exportação está em andamento. Você será notificado quando terminar."
|
||||||
|
actions: "Ações"
|
||||||
|
block_reservations: "Impedir reservas"
|
||||||
|
do_you_really_want_to_block_this_slot: "Você realmente quer bloquear novas reservas neste slot? Isso se tornará invisível para os usuários."
|
||||||
|
locking_success: "Slot bloqueado com êxito, ela não aparecerá mais tempo no calendário do usuário"
|
||||||
|
locking_failed: "Um erro ocorreu. O bloqueio do slot falhou"
|
||||||
|
allow_reservations: "Permitir a reserva"
|
||||||
|
do_you_really_want_to_allow_reservations: "Você realmente quer permitir a reserva novamente neste slot? Será visível para os usuários."
|
||||||
|
unlocking_success: "Slot desbloqueado com sucesso, ele aparecerá novamente no calendário do usuário"
|
||||||
|
unlocking_failed: "Um erro ocorreu. O desbloqueio do slot falhou"
|
||||||
|
reservations_locked: "Reserva é bloqueado"
|
||||||
|
unlockable_because_reservations: "Não é possível bloquear a reserva neste slot porque existem algumas reservas não cancelados nele."
|
||||||
|
|
||||||
project_elements:
|
project_elements:
|
||||||
# management of the projects' components
|
# management of the projects' components
|
||||||
|
@ -85,6 +85,7 @@ Rails.application.routes.draw do
|
|||||||
get 'reservations', on: :member
|
get 'reservations', on: :member
|
||||||
get 'public', on: :collection
|
get 'public', on: :collection
|
||||||
get '/export_index', action: 'export_availabilities', on: :collection
|
get '/export_index', action: 'export_availabilities', on: :collection
|
||||||
|
put ':id/lock', action: 'lock', on: :collection
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :groups, only: [:index, :create, :update, :destroy]
|
resources :groups, only: [:index, :create, :update, :destroy]
|
||||||
|
5
db/migrate/20170906100906_add_lock_to_availability.rb
Normal file
5
db/migrate/20170906100906_add_lock_to_availability.rb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class AddLockToAvailability < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :availabilities, :lock, :boolean, default: false
|
||||||
|
end
|
||||||
|
end
|
@ -11,7 +11,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20170227114634) do
|
ActiveRecord::Schema.define(version: 20170906100906) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@ -79,6 +79,7 @@ ActiveRecord::Schema.define(version: 20170227114634) do
|
|||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "nb_total_places"
|
t.integer "nb_total_places"
|
||||||
t.boolean "destroying", default: false
|
t.boolean "destroying", default: false
|
||||||
|
t.boolean "lock", default: false
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "availability_tags", force: :cascade do |t|
|
create_table "availability_tags", force: :cascade do |t|
|
||||||
|
Loading…
Reference in New Issue
Block a user