mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-22 11:52:21 +01:00
(merge) merge branch pre_inscription
This commit is contained in:
commit
3f1d6bf378
@ -10,7 +10,7 @@ class API::ChildrenController < API::APIController
|
||||
authorize Child
|
||||
user_id = current_user.id
|
||||
user_id = params[:user_id] if current_user.privileged? && params[:user_id]
|
||||
@children = Child.where(user_id: user_id).includes(:supporting_document_files).order(:created_at)
|
||||
@children = Child.where(user_id: user_id).where('birthday >= ?', 18.years.ago).includes(:supporting_document_files).order(:created_at)
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -5,7 +5,7 @@
|
||||
# availability by Availability.slot_duration, or otherwise globally by Setting.get('slot_duration')
|
||||
class API::SlotsReservationsController < API::APIController
|
||||
before_action :authenticate_user!
|
||||
before_action :set_slots_reservation, only: %i[update cancel validate]
|
||||
before_action :set_slots_reservation, only: %i[update cancel validate invalidate]
|
||||
respond_to :json
|
||||
|
||||
def update
|
||||
@ -28,6 +28,11 @@ class API::SlotsReservationsController < API::APIController
|
||||
SlotsReservationsService.validate(@slot_reservation)
|
||||
end
|
||||
|
||||
def invalidate
|
||||
authorize @slot_reservation
|
||||
SlotsReservationsService.invalidate(@slot_reservation)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_slots_reservation
|
||||
|
@ -100,7 +100,7 @@ export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, on
|
||||
* Callback triggered when the user validates the machine form: handle create or update
|
||||
*/
|
||||
const onSubmit: SubmitHandler<Event> = (data: Event) => {
|
||||
if (data.pre_registration_end_date.toString() === 'Invalid Date') {
|
||||
if (data.pre_registration_end_date?.toString() === 'Invalid Date') {
|
||||
data.pre_registration_end_date = null;
|
||||
}
|
||||
if (action === 'update') {
|
||||
|
@ -44,9 +44,9 @@ export const EventReservationItem: React.FC<EventReservationItemProps> = ({ rese
|
||||
* Return the pre-registration status
|
||||
*/
|
||||
const preRegistrationStatus = () => {
|
||||
if (!reservation.validated_at && !reservation.canceled_at && !reservation.is_paid) {
|
||||
if (!reservation.is_valid && !reservation.canceled_at && !reservation.is_paid) {
|
||||
return t('app.logged.event_reservation_item.in_the_process_of_validation');
|
||||
} else if (reservation.validated_at && !reservation.canceled_at && !reservation.is_paid) {
|
||||
} else if (reservation.is_valid && !reservation.canceled_at && !reservation.is_paid) {
|
||||
return t('app.logged.event_reservation_item.settle_your_payment');
|
||||
} else if (reservation.is_paid && !reservation.canceled_at) {
|
||||
return t('app.logged.event_reservation_item.paid');
|
||||
|
@ -458,7 +458,7 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
|
||||
* @returns {boolean}
|
||||
*/
|
||||
$scope.isValidated = function (reservation) {
|
||||
return !!(reservation.slots_reservations_attributes[0].validated_at);
|
||||
return reservation.slots_reservations_attributes[0].is_valid === true || reservation.slots_reservations_attributes[0].is_valid === 'true';
|
||||
};
|
||||
|
||||
/**
|
||||
@ -466,25 +466,30 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
|
||||
* @param reservation {Reservation}
|
||||
*/
|
||||
$scope.validateReservation = function (reservation) {
|
||||
dialogs.confirm({
|
||||
resolve: {
|
||||
object: function () {
|
||||
return {
|
||||
title: _t('app.admin.event_reservations.validate_the_reservation'),
|
||||
msg: _t('app.admin.event_reservations.do_you_really_want_to_validate_this_reservation_this_apply_to_all_booked_tickets')
|
||||
};
|
||||
}
|
||||
}
|
||||
}, function () { // validate confirmed
|
||||
SlotsReservation.validate({
|
||||
id: reservation.slots_reservations_attributes[0].id
|
||||
}, () => { // successfully validated
|
||||
growl.success(_t('app.admin.event_reservations.reservation_was_successfully_validated'));
|
||||
const index = $scope.reservations.indexOf(reservation);
|
||||
$scope.reservations[index].slots_reservations_attributes[0].validated_at = new Date();
|
||||
}, () => {
|
||||
growl.warning(_t('app.admin.event_reservations.validation_failed'));
|
||||
});
|
||||
SlotsReservation.validate({
|
||||
id: reservation.slots_reservations_attributes[0].id
|
||||
}, () => { // successfully validated
|
||||
growl.success(_t('app.admin.event_reservations.reservation_was_successfully_validated'));
|
||||
const index = $scope.reservations.indexOf(reservation);
|
||||
$scope.reservations[index].slots_reservations_attributes[0].is_valid = true;
|
||||
}, () => {
|
||||
growl.warning(_t('app.admin.event_reservations.validation_failed'));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to invalidate a reservation
|
||||
* @param reservation {Reservation}
|
||||
*/
|
||||
$scope.invalidateReservation = function (reservation) {
|
||||
SlotsReservation.invalidate({
|
||||
id: reservation.slots_reservations_attributes[0].id
|
||||
}, () => { // successfully validated
|
||||
growl.success(_t('app.admin.event_reservations.reservation_was_successfully_invalidated'));
|
||||
const index = $scope.reservations.indexOf(reservation);
|
||||
$scope.reservations[index].slots_reservations_attributes[0].is_valid = false;
|
||||
}, () => {
|
||||
growl.warning(_t('app.admin.event_reservations.invalidation_failed'));
|
||||
});
|
||||
};
|
||||
|
||||
@ -511,6 +516,9 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
|
||||
templateUrl: '/admin/events/pay_reservation_modal.html',
|
||||
size: 'sm',
|
||||
resolve: {
|
||||
event () {
|
||||
return $scope.event;
|
||||
},
|
||||
reservation () {
|
||||
return reservation;
|
||||
},
|
||||
@ -524,8 +532,10 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
|
||||
return mkCartItems(reservation);
|
||||
}
|
||||
},
|
||||
controller: ['$scope', '$uibModalInstance', 'reservation', 'price', 'wallet', 'cartItems', 'helpers', '$filter', '_t', 'Reservation',
|
||||
function ($scope, $uibModalInstance, reservation, price, wallet, cartItems, helpers, $filter, _t, Reservation) {
|
||||
controller: ['$scope', '$uibModalInstance', 'reservation', 'price', 'wallet', 'cartItems', 'helpers', '$filter', '_t', 'Reservation', 'event',
|
||||
function ($scope, $uibModalInstance, reservation, price, wallet, cartItems, helpers, $filter, _t, Reservation, event) {
|
||||
$scope.event = event;
|
||||
|
||||
// User's wallet amount
|
||||
$scope.wallet = wallet;
|
||||
|
||||
@ -609,7 +619,11 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
|
||||
if (r.id === reservation.id) {
|
||||
return reservation;
|
||||
}
|
||||
growl.success(_t('app.admin.event_reservations.reservation_was_successfully_paid'));
|
||||
if ($scope.event.amount === 0) {
|
||||
growl.success(_t('app.admin.event_reservations.reservation_was_successfully_present'));
|
||||
} else {
|
||||
growl.success(_t('app.admin.event_reservations.reservation_was_successfully_paid'));
|
||||
}
|
||||
return r;
|
||||
});
|
||||
}, function () {
|
||||
|
@ -644,7 +644,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
if (!user.booked) {
|
||||
return false;
|
||||
}
|
||||
if ($scope.enableChildValidationRequired && user.booked.type === 'Child' && !user.booked.validatedAt) {
|
||||
if ($scope.enableChildValidationRequired && user.booked.type === 'Child' && !user.booked.validated_at) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -675,9 +675,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
|
||||
// get the current user's reservations into $scope.reservations
|
||||
if ($scope.currentUser) {
|
||||
getReservations($scope.event.id, 'Event', $scope.currentUser.id);
|
||||
getChildren($scope.currentUser.id).then(function (children) {
|
||||
updateNbReservePlaces();
|
||||
getReservations($scope.event.id, 'Event', $scope.currentUser.id).then(function (reservations) {
|
||||
getChildren($scope.currentUser.id).then(function (children) {
|
||||
updateNbReservePlaces();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -696,7 +697,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
* @param user_id {number} the user's id (current or managed)
|
||||
*/
|
||||
const getReservations = function (reservable_id, reservable_type, user_id) {
|
||||
Reservation.query({
|
||||
return Reservation.query({
|
||||
reservable_id,
|
||||
reservable_type,
|
||||
user_id
|
||||
@ -727,6 +728,14 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const r of $scope.reservations) {
|
||||
for (const user of r.booking_users_attributes) {
|
||||
const key = user.booked_type === 'User' ? `user_${user.booked_id}` : `child_${user.booked_id}`;
|
||||
if (key === userKey) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -748,7 +757,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
name: child.first_name + ' ' + child.last_name,
|
||||
id: child.id,
|
||||
type: 'Child',
|
||||
validatedAt: child.validated_at,
|
||||
validated_at: child.validated_at,
|
||||
birthday: child.birthday
|
||||
});
|
||||
}
|
||||
@ -761,7 +770,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
*/
|
||||
const updateNbReservePlaces = function () {
|
||||
if ($scope.event.event_type === 'family') {
|
||||
const maxPlaces = $scope.children.length + 1;
|
||||
const reservedPlaces = $scope.reservations.reduce((sum, reservation) => {
|
||||
return sum + reservation.booking_users_attributes.length;
|
||||
}, 0);
|
||||
const maxPlaces = $scope.children.length + 1 - reservedPlaces;
|
||||
if ($scope.event.nb_free_places > maxPlaces) {
|
||||
$scope.reserve.nbPlaces.normal = __range__(0, maxPlaces, true);
|
||||
for (let evt_px_cat of Array.from($scope.event.event_price_categories_attributes)) {
|
||||
@ -976,11 +988,11 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
Reservation.get({ id: invoice.main_object.id }, function (reservation) {
|
||||
$scope.event.nb_free_places = $scope.event.nb_free_places - reservation.total_booked_seats;
|
||||
$scope.reservations.push(reservation);
|
||||
resetEventReserve();
|
||||
updateNbReservePlaces();
|
||||
$scope.reserveSuccess = true;
|
||||
$scope.coupon.applied = null;
|
||||
});
|
||||
resetEventReserve();
|
||||
updateNbReservePlaces();
|
||||
$scope.reserveSuccess = true;
|
||||
$scope.coupon.applied = null;
|
||||
if ($scope.currentUser.role === 'admin') {
|
||||
return $scope.ctrl.member = null;
|
||||
}
|
||||
|
@ -70,7 +70,8 @@ export interface Reservation {
|
||||
event_type?: string,
|
||||
event_title?: string,
|
||||
event_pre_registration?: boolean
|
||||
validated_at?: TDateISO,
|
||||
canceled_at?: TDateISO,
|
||||
is_valid?: boolean,
|
||||
is_paid?: boolean,
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,10 @@ Application.Services.factory('SlotsReservation', ['$resource', function ($resour
|
||||
validate: {
|
||||
method: 'PUT',
|
||||
url: '/api/slots_reservations/:id/validate'
|
||||
},
|
||||
invalidate: {
|
||||
method: 'PUT',
|
||||
url: '/api/slots_reservations/:id/invalidate'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -1,45 +1,52 @@
|
||||
<div class="modal-header">
|
||||
<img ng-src="{{logoBlack.custom_asset_file_attributes.attachment_url}}" alt="{{logo.custom_asset_file_attributes.attachment}}" class="modal-logo"/>
|
||||
<h1 translate ng-show="reservation">{{ 'app.admin.event_reservations.confirm_payment' }}</h1>
|
||||
<h1 translate ng-show="reservation && event.amount !== 0">{{ 'app.admin.event_reservations.confirm_payment' }}</h1>
|
||||
<h1 translate ng-show="reservation && event.amount === 0">{{ 'app.admin.event_reservations.confirm_present' }}</h1>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<uib-alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</uib-alert>
|
||||
<div class="row" ng-show="!offered">
|
||||
<wallet-info current-user="currentUser"
|
||||
cart="cartItems"
|
||||
price="price.price"
|
||||
wallet="wallet"/>
|
||||
</div>
|
||||
<div class="row m-b">
|
||||
<div class="col-md-12">
|
||||
<label for="offerSlot" class="control-label m-r" translate>{{ 'app.admin.event_reservations.offer_this_reservation' }}</label>
|
||||
<input bs-switch
|
||||
ng-model="offered"
|
||||
id="offerSlot"
|
||||
type="checkbox"
|
||||
class="form-control"
|
||||
switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
|
||||
switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
|
||||
switch-animate="true"
|
||||
ng-change="computeEventAmount()"/>
|
||||
<div ng-show="event.amount !== 0">
|
||||
<div class="row" ng-show="!offered">
|
||||
<wallet-info current-user="currentUser"
|
||||
cart="cartItems"
|
||||
price="price.price"
|
||||
wallet="wallet"/>
|
||||
</div>
|
||||
<div class="row m-b">
|
||||
<div class="col-md-12">
|
||||
<label for="offerSlot" class="control-label m-r" translate>{{ 'app.admin.event_reservations.offer_this_reservation' }}</label>
|
||||
<input bs-switch
|
||||
ng-model="offered"
|
||||
id="offerSlot"
|
||||
type="checkbox"
|
||||
class="form-control"
|
||||
switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
|
||||
switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
|
||||
switch-animate="true"
|
||||
ng-change="computeEventAmount()"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<coupon show="true" coupon="coupon.applied" total="price.price_without_coupon" user-id="{{reservation.user_id}}"></coupon>
|
||||
<coupon show="true" coupon="coupon.applied" total="price.price_without_coupon" user-id="{{reservation.user_id}}"></coupon>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group col-sm-12">
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox"
|
||||
name="paymentReceived"
|
||||
id="paymentReceived"
|
||||
ng-model="payment" />
|
||||
<label for="paymentReceived" translate>{{ 'app.admin.event_reservations.i_have_received_the_payment' }}</label>
|
||||
<div class="row">
|
||||
<div class="form-group col-sm-12">
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox"
|
||||
name="paymentReceived"
|
||||
id="paymentReceived"
|
||||
ng-model="payment" />
|
||||
<label for="paymentReceived" translate>{{ 'app.admin.event_reservations.i_have_received_the_payment' }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="event.amount === 0">
|
||||
<p translate>{{ 'app.admin.event_reservations.confirm_present_info' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-info" ng-click="ok()" ng-disabled="attempting || !payment" ng-bind-html="validButtonName"></button>
|
||||
<button class="btn btn-info" ng-if="event.amount !== 0" ng-click="ok()" ng-disabled="attempting || !payment" ng-bind-html="validButtonName"></button>
|
||||
<button class="btn btn-info" ng-if="event.amount === 0" ng-click="ok()" ng-disabled="attempting" ng-bind-html="validButtonName"></button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
|
@ -20,13 +20,13 @@
|
||||
<table class="table" ng-if="reservations.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:25%" translate>{{ 'app.admin.event_reservations.booked_by' }}</th>
|
||||
<th style="width:25%" translate>{{ 'app.admin.event_reservations.reservations' }}</th>
|
||||
<th style="width:25%" translate>{{ 'app.admin.event_reservations.date' }}</th>
|
||||
<th style="width:25%" translate>{{ 'app.admin.event_reservations.reserved_tickets' }}</th>
|
||||
<th ng-if="event.pre_registration" style="width:25%" translate>{{ 'app.admin.event_reservations.status' }}</th>
|
||||
<th ng-if="event.pre_registration" style="width:25%" translate>{{ 'app.admin.event_reservations.gestion' }}</th>
|
||||
<th style="width:5%"></th>
|
||||
<th translate>{{ 'app.admin.event_reservations.booked_by' }}</th>
|
||||
<th translate>{{ 'app.admin.event_reservations.reservations' }}</th>
|
||||
<th translate>{{ 'app.admin.event_reservations.date' }}</th>
|
||||
<th translate>{{ 'app.admin.event_reservations.reserved_tickets' }}</th>
|
||||
<th ng-if="event.pre_registration" translate>{{ 'app.admin.event_reservations.status' }}</th>
|
||||
<th ng-if="event.pre_registration" translate>{{ 'app.admin.event_reservations.validation' }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -38,6 +38,7 @@
|
||||
<span ng-if="event.event_type === 'standard'">{{ reservation.user_full_name }} </span>
|
||||
<div ng-repeat="bu in reservation.booking_users_attributes">
|
||||
<span>{{bu.name}}</span>
|
||||
<span ng-if="bu.booked_type === 'Child'" class="m-l-sm">({{ 'app.admin.event_reservations.age' | translate:{NUMBER: bu.age} }})</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ reservation.created_at | amDateFormat:'LL LTS' }}</td>
|
||||
@ -48,16 +49,27 @@
|
||||
<td ng-if="event.pre_registration">
|
||||
<span ng-if="!isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" class="v-middle badge text-sm bg-info" translate="">{{ 'app.admin.event_reservations.event_status.pre_registered' }}</span>
|
||||
<span ng-if="isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" class="v-middle badge text-sm bg-stage" translate="">{{ 'app.admin.event_reservations.event_status.to_pay' }}</span>
|
||||
<span ng-if="reservation.is_paid && !isCancelled(reservation)" class="v-middle badge text-sm bg-success" translate="">{{ 'app.admin.event_reservations.event_status.paid' }}</span>
|
||||
<span ng-if="event.amount !== 0 && reservation.is_paid && !isCancelled(reservation)" class="v-middle badge text-sm bg-success" translate="">{{ 'app.admin.event_reservations.event_status.paid' }}</span>
|
||||
<span ng-if="event.amount === 0 && reservation.is_paid && !isCancelled(reservation)" class="v-middle badge text-sm bg-success" translate="">{{ 'app.admin.event_reservations.event_status.present' }}</span>
|
||||
<span ng-if="isCancelled(reservation)" class="v-middle badge text-sm bg-event" translate="">{{ 'app.admin.event_reservations.event_status.canceled' }}</span>
|
||||
</td>
|
||||
<td ng-if="event.pre_registration">
|
||||
<button class="btn btn-default" ng-click="validateReservation(reservation)" ng-if="!isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" translate>
|
||||
{{ 'app.admin.event_reservations.validate' }}
|
||||
</button>
|
||||
<button class="btn btn-default" ng-click="payReservation(reservation)" ng-if="isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" translate>
|
||||
{{ 'app.admin.event_reservations.pay' }}
|
||||
</button>
|
||||
<div>
|
||||
<div ng-if="!isCancelled(reservation) && !reservation.is_paid">
|
||||
<label class="m-r-sm">
|
||||
<span translate>{{ 'app.admin.event_reservations.negative' }}</span>
|
||||
<input type="radio" name="validate" ng-value="false" ng-click="invalidateReservation(reservation)" ng-model="reservation.slots_reservations_attributes[0].is_valid" >
|
||||
</label>
|
||||
<label>
|
||||
<span translate>{{ 'app.admin.event_reservations.affirmative' }}</span>
|
||||
<input type="radio" name="validate" ng-value="true" ng-click="validateReservation(reservation)" ng-model="reservation.slots_reservations_attributes[0].is_valid" >
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-default" ng-click="payReservation(reservation)" ng-if="isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid">
|
||||
<span ng-if="event.amount !== 0" translate>{{ 'app.admin.event_reservations.pay' }}</span>
|
||||
<span ng-if="event.amount === 0" translate>{{ 'app.admin.event_reservations.present' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="buttons">
|
||||
|
@ -142,7 +142,7 @@
|
||||
class="form-control">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
<uib-alert type="danger" ng-if="enableChildValidationRequired && user.booked && user.booked.type === 'Child' && !user.booked.validatedAt" style="margin-bottom: 0.8rem;">
|
||||
<uib-alert type="danger" ng-if="enableChildValidationRequired && user.booked && user.booked.type === 'Child' && !user.booked.validated_at" style="margin-bottom: 0.8rem;">
|
||||
<span class="text-sm">
|
||||
<i class="fa fa-warning"></i>
|
||||
<span translate>{{ 'app.shared.cart.child_validation_required_alert' }}</span>
|
||||
@ -180,7 +180,7 @@
|
||||
class="form-control">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
<uib-alert type="danger" ng-if="enableChildValidationRequired && user.booked && user.booked.type === 'Child' && !user.booked.validatedAt">
|
||||
<uib-alert type="danger" ng-if="enableChildValidationRequired && user.booked && user.booked.type === 'Child' && !user.booked.is_valid">
|
||||
<p class="text-sm">
|
||||
<i class="fa fa-warning"></i>
|
||||
<span translate>{{ 'app.shared.cart.child_validation_required_alert' }}</span>
|
||||
|
@ -89,7 +89,7 @@ class Event < ApplicationRecord
|
||||
else
|
||||
reserved_places = reservations.joins(:slots_reservations)
|
||||
.where('slots_reservations.canceled_at': nil)
|
||||
.where.not('slots_reservations.validated_at': nil)
|
||||
.where('slots_reservations.is_valid': true)
|
||||
.map(&:total_booked_seats)
|
||||
.inject(0) { |sum, t| sum + t }
|
||||
self.nb_free_places = (nb_total_places - reserved_places)
|
||||
@ -99,7 +99,7 @@ class Event < ApplicationRecord
|
||||
def nb_places_for_pre_registration
|
||||
reservations.joins(:slots_reservations)
|
||||
.where('slots_reservations.canceled_at': nil)
|
||||
.where('slots_reservations.validated_at': nil)
|
||||
.where('slots_reservations.is_valid': nil)
|
||||
.map(&:total_booked_seats)
|
||||
.inject(0) { |sum, t| sum + t }
|
||||
end
|
||||
|
@ -19,4 +19,8 @@ class SlotsReservationPolicy < ApplicationPolicy
|
||||
def validate?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
|
||||
def invalidate?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
end
|
||||
|
@ -23,7 +23,7 @@ class SlotsReservationsService
|
||||
end
|
||||
|
||||
def validate(slot_reservation)
|
||||
if slot_reservation.update(validated_at: Time.current)
|
||||
if slot_reservation.update(is_valid: true)
|
||||
reservable = slot_reservation.reservation.reservable
|
||||
if reservable.is_a?(Event)
|
||||
reservable.update_nb_free_places
|
||||
@ -39,5 +39,23 @@ class SlotsReservationsService
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def invalidate(slot_reservation)
|
||||
if slot_reservation.update(is_valid: false)
|
||||
reservable = slot_reservation.reservation.reservable
|
||||
if reservable.is_a?(Event)
|
||||
reservable.update_nb_free_places
|
||||
reservable.save
|
||||
end
|
||||
NotificationCenter.call type: 'notify_member_reservation_invalidated',
|
||||
receiver: slot_reservation.reservation.user,
|
||||
attached_object: slot_reservation.reservation
|
||||
NotificationCenter.call type: 'notify_admin_reservation_invalidated',
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: slot_reservation.reservation
|
||||
return true
|
||||
end
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -18,7 +18,7 @@ json.array!(@members) do |member|
|
||||
end
|
||||
end
|
||||
json.validated_at member.validated_at
|
||||
json.children member.children.order(:created_at) do |child|
|
||||
json.children member.children.where('birthday >= ?', 18.years.ago).order(:created_at) do |child|
|
||||
json.extract! child, :id, :first_name, :last_name, :email, :birthday, :phone, :user_id, :validated_at
|
||||
json.supporting_document_files_attributes child.supporting_document_files do |f|
|
||||
json.id f.id
|
||||
|
@ -87,7 +87,7 @@ json.events_reservations @member.reservations.where(reservable_type: 'Event').jo
|
||||
json.event_type sr.reservation.reservable.event_type
|
||||
json.event_title sr.reservation.reservable.title
|
||||
json.event_pre_registration sr.reservation.reservable.pre_registration
|
||||
json.validated_at sr.validated_at
|
||||
json.is_valid sr.is_valid
|
||||
json.is_paid sr.reservation.invoice_items.count.positive?
|
||||
json.canceled_at sr.canceled_at
|
||||
json.booking_users_attributes sr.reservation.booking_users.order(booked_type: :desc) do |bu|
|
||||
|
@ -0,0 +1,4 @@
|
||||
json.title notification.notification_type
|
||||
json.description t('.a_RESERVABLE_reservation_was_invalidated_html',
|
||||
RESERVABLE: notification.attached_object.reservable.name,
|
||||
NAME: notification.attached_object.user&.profile&.full_name || t('api.notifications.deleted_user'))
|
@ -0,0 +1,3 @@
|
||||
json.title notification.notification_type
|
||||
json.description t('.your_reservation_RESERVABLE_was_invalidated_html',
|
||||
RESERVABLE: notification.attached_object.reservable.name)
|
@ -0,0 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.title notification.notification_type
|
||||
json.description t('.child_age_will_be_18_years_ago',
|
||||
NAME: notification.attached_object.full_name,
|
||||
DATE: I18n.l(notification.attached_object.birthday, format: :default))
|
@ -7,7 +7,7 @@ json.message reservation.message
|
||||
json.slots_reservations_attributes reservation.slots_reservations do |sr|
|
||||
json.id sr.id
|
||||
json.canceled_at sr.canceled_at&.iso8601
|
||||
json.validated_at sr.validated_at&.iso8601
|
||||
json.is_valid sr.is_valid
|
||||
json.slot_id sr.slot_id
|
||||
json.slot_attributes do
|
||||
json.id sr.slot_id
|
||||
@ -40,5 +40,7 @@ json.booking_users_attributes reservation.booking_users.order(booked_type: :desc
|
||||
json.event_price_category_id bu.event_price_category_id
|
||||
json.booked_id bu.booked_id
|
||||
json.booked_type bu.booked_type
|
||||
json.age ((Time.zone.now - bu.booked.birthday.to_time) / 1.year.seconds).floor if bu.booked_type == 'Child'
|
||||
end
|
||||
json.is_valid reservation.slots_reservations[0].is_valid
|
||||
json.is_paid reservation.invoice_items.count.positive?
|
||||
|
@ -0,0 +1,7 @@
|
||||
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
|
||||
|
||||
<p>
|
||||
<%= t('.body.reservation_invalidated_html',
|
||||
NAME: @attached_object.user&.profile&.full_name || t('api.notifications.deleted_user'),
|
||||
RESERVABLE: @attached_object.reservable.name) %>
|
||||
</p>
|
@ -0,0 +1,3 @@
|
||||
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
|
||||
|
||||
<p><%= t('.body.reservation_invalidated_html', RESERVATION: @attached_object.reservable.name) %></p>
|
@ -0,0 +1,6 @@
|
||||
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
|
||||
|
||||
<%= t('.body.child_age_will_be_18_years_ago', **{
|
||||
NAME: @attached_object.full_name,
|
||||
DATE: I18n.l(@attached_object.birthday, format: :default),
|
||||
}) %>
|
15
app/workers/child_age_worker.rb
Normal file
15
app/workers/child_age_worker.rb
Normal file
@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# send a notification if child age > 18 years ago
|
||||
class ChildAgeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform
|
||||
children = Child.where('birthday = ?', 18.years.ago + 2.days)
|
||||
children.each do |child|
|
||||
NotificationCenter.call type: 'notify_user_when_child_age_will_be_18',
|
||||
receiver: child.user,
|
||||
attached_object: child
|
||||
end
|
||||
end
|
||||
end
|
@ -647,22 +647,33 @@ en:
|
||||
reservations: "Reservations"
|
||||
status: "Status"
|
||||
gestion: "Gestion"
|
||||
validation: "Validation"
|
||||
event_status:
|
||||
pre_registered: "Pre-registered"
|
||||
to_pay: "To pay"
|
||||
paid: "Paid"
|
||||
canceled: "Canceled"
|
||||
present: "Present"
|
||||
affirmative: "yes"
|
||||
negative: "no"
|
||||
validate: "Validate"
|
||||
pay: "Pay"
|
||||
validate_the_reservation: "Validate the reservation"
|
||||
do_you_really_want_to_validate_this_reservation_this_apply_to_all_booked_tickets: "Do you really want to validate this reservation? This apply to ALL booked tickets."
|
||||
reservation_was_successfully_validated: "Reservation was successfully validated."
|
||||
validation_failed: "Validation failed."
|
||||
reservation_was_successfully_invalidated: "Reservation was successfully invalidated."
|
||||
invalidation_failed: "Invalidation failed."
|
||||
confirm_payment: "Confirm payment"
|
||||
confirm_payment_of_html: "{ROLE, select, admin{Cash} other{Pay}}: {AMOUNT}" #(contexte : validate a payment of $20,00)
|
||||
offer_this_reservation: "I offer this reservation"
|
||||
i_have_received_the_payment: "I have received the payment"
|
||||
reservation_was_successfully_paid: "Reservation was successfully paid."
|
||||
present: "Present"
|
||||
confirm_present: "Confirm presence"
|
||||
confirm_present_info: "Confirm the presence of the user for this event"
|
||||
reservation_was_successfully_present: "The presence of the user was successfully confirmed."
|
||||
age: "{NUMBER} years old"
|
||||
events_settings:
|
||||
title: "Settings"
|
||||
generic_text_block: "Editorial text block"
|
||||
|
@ -647,22 +647,33 @@ fr:
|
||||
reservations: "Réservations"
|
||||
status: "Statut"
|
||||
gestion: "Gestion"
|
||||
validation: "Validation"
|
||||
event_status:
|
||||
pre_registered: "Pré-inscrit"
|
||||
to_pay: "À payer"
|
||||
paid: "Payé"
|
||||
canceled: "Annulée"
|
||||
present: "Présent"
|
||||
affirmative: "Oui"
|
||||
negative: "Non"
|
||||
validate: "Valider"
|
||||
pay: "Payer"
|
||||
validate_the_reservation: "Valider la réservation"
|
||||
do_you_really_want_to_validate_this_reservation_this_apply_to_all_booked_tickets: "Êtes vous sur de vouloir valider cette réservation? Ceci s'applique à TOUTES les places réservées."
|
||||
reservation_was_successfully_validated: "La réservation a bien été validé."
|
||||
validation_failed: "La validation a échoué."
|
||||
reservation_was_successfully_invalidated: "La réservation a bien été invalidée."
|
||||
invalidation_failed: "L'invalidation a échoué."
|
||||
confirm_payment: "Confirmer le paiement"
|
||||
confirm_payment_of_html: "{ROLE, select, admin{Encaisser} other{Payer}} : {AMOUNT}" #(contexte : validate a payment of $20,00)
|
||||
offer_this_reservation: "J'offre cette réservation"
|
||||
i_have_received_the_payment: "J'ai reçu le paiement"
|
||||
reservation_was_successfully_paid: "La réservation a bien été payée."
|
||||
present: "Présenter"
|
||||
confirm_present: "Confirmer la présence"
|
||||
confirm_present_info: "Confirmer la présence de l'utilisateur à l'événement"
|
||||
reservation_was_successfully_present: "La présence a bien été confirmée."
|
||||
age: "{NUMBER} ans"
|
||||
events_settings:
|
||||
title: "Paramètres"
|
||||
generic_text_block: "Bloc de texte rédactionnel"
|
||||
|
@ -336,9 +336,9 @@ fr:
|
||||
ticket: "{NUMBER, plural, =0{place} one{place} other{places}}"
|
||||
make_a_gift_of_this_reservation: "Offrir cette réservation"
|
||||
thank_you_your_payment_has_been_successfully_registered: "Merci. Votre paiement a bien été pris en compte !"
|
||||
thank_you_your_pre_registration_has_been_successfully_saved: "Merci. Votre demande a bien été pris en compte !"
|
||||
thank_you_your_pre_registration_has_been_successfully_saved: "Merci. Votre demande a bien été prise en compte !"
|
||||
you_can_find_your_reservation_s_details_on_your_: "Vous pouvez retrouver le détail de votre réservation sur votre"
|
||||
informed_by_email_your_pre_registration: "vous serez tenu informé par email de la suite donnée à votre pré-inscription"
|
||||
informed_by_email_your_pre_registration: "Vous serez tenu informé par email de la suite donnée à votre pré-inscription"
|
||||
dashboard: "tableau de bord"
|
||||
you_booked_DATE: "Vous avez réservé ({DATE}) :"
|
||||
you_pre_booked_DATE: "Votre pré-inscription ({DATE}) :"
|
||||
|
@ -477,6 +477,12 @@ en:
|
||||
your_reservation_RESERVABLE_was_validated_html: "Your reservation <strong><em>%{RESERVABLE}</em></strong> was successfully validated."
|
||||
notify_admin_reservation_validated:
|
||||
a_RESERVABLE_reservation_was_validated_html: "A <strong><em>%{RESERVABLE}</em></strong> reservation of <strong><em>%{USER}</em></strong> was validated."
|
||||
notify_member_reservation_invalidated:
|
||||
your_reservation_RESERVABLE_was_invalidated_html: "Your pre-registration of <strong><em>%{RESERVABLE}</em></strong> wasn't validated."
|
||||
notify_admin_reservation_invalidated:
|
||||
a_RESERVABLE_reservation_was_invalidated_html: "A <strong><em>%{RESERVABLE}</em></strong> pre-registration of <strong><em>%{USER}</em></strong> was invalidated."
|
||||
notify_user_when_child_age_will_be_18:
|
||||
child_age_will_be_18_years_ago: "Your child %{NAME} will turn 18 on %{DATE}, at which point they will be automatically detached from your Family account. They will need to create their own account in order to make reservations."
|
||||
#statistics tools for admins
|
||||
statistics:
|
||||
subscriptions: "Subscriptions"
|
||||
|
@ -477,6 +477,12 @@ fr:
|
||||
your_reservation_RESERVABLE_was_validated_html: "Votre réservation de <strong><em>%{RESERVABLE}</em></strong> a été validée."
|
||||
notify_admin_reservation_validated:
|
||||
a_RESERVABLE_reservation_was_validated_html: "La réservation de <strong><em>%{RESERVABLE}</em></strong> de <strong><em>%{NAME}</em></strong> a été validée."
|
||||
notify_member_reservation_invalidated:
|
||||
your_reservation_RESERVABLE_was_invalidated_html: "Votre demande de pré-inscription de <strong><em>%{RESERVABLE}</em></strong> n'a pas été validée."
|
||||
notify_admin_reservation_invalidated:
|
||||
a_RESERVABLE_reservation_was_invalidated_html: "La réservation de <strong><em>%{RESERVABLE}</em></strong> de <strong><em>%{NAME}</em></strong> a été invalidée."
|
||||
notify_user_when_child_age_will_be_18:
|
||||
child_age_will_be_18_years_ago: "Votre enfant %{NAME} va avoir 18ans, le %{DATE}, date à laquelle il sera automatiquement détaché de votre compte Famille. Il devra se créer son propre compte pour effectuer ses réservations."
|
||||
#statistics tools for admins
|
||||
statistics:
|
||||
subscriptions: "Abonnements"
|
||||
|
@ -467,7 +467,19 @@ en:
|
||||
reservation_validated_html: "<strong><em>%{RESERVABLE}</em></strong> was validated."
|
||||
your_reserved_slots: "Your reserved slots are:"
|
||||
notify_admin_reservation_validated:
|
||||
subject: "Réservation a bien été validé"
|
||||
subject: "Pre-registration was validated"
|
||||
body:
|
||||
reservation_validated_html: "<strong><em>%{RESERVABLE}</em></strong> of %{NAME} was validated."
|
||||
reserved_slots: "Reserved slots are:"
|
||||
notify_member_reservation_invalidated:
|
||||
subject: "Your pre-registration wasn't validated"
|
||||
body:
|
||||
reservation_invalidated_html: "<strong><em>%{RESERVABLE}</em></strong> wasn't validated."
|
||||
notify_admin_reservation_invalidated:
|
||||
subject: "Pre-registration wasn't validated"
|
||||
body:
|
||||
reservation_invalidated_html: "<strong><em>%{RESERVABLE}</em></strong> of %{NAME} wasn't validated."
|
||||
notify_user_when_child_age_will_be_18:
|
||||
subject: "Your child will be 18 years old"
|
||||
body:
|
||||
child_age_will_be_18_years_ago: "Your child %{NAME} will turn 18 on %{DATE}, at which point they will be automatically detached from your Family account. They will need to create their own account in order to make reservations."
|
||||
|
@ -471,3 +471,15 @@ fr:
|
||||
body:
|
||||
reservation_validated_html: "<strong><em>%{RESERVABLE}</em></strong> du membre %{NAME} a bien été validé."
|
||||
reserved_slots: "Les créneaux réservés sont :"
|
||||
notify_member_reservation_invalidated:
|
||||
subject: "Votre demande of pré-inscription n'a pas été validée"
|
||||
body:
|
||||
reservation_invalidated_html: "Votre réservation <strong><em>%{RESERVATION}</em></strong> n'a pas été validée."
|
||||
notify_admin_reservation_invalidated:
|
||||
subject: "Demande of pré-inscription n'a pas été validée"
|
||||
body:
|
||||
reservation_invalidated_html: "<strong><em>%{RESERVABLE}</em></strong> du membre %{NAME} n'a pas été validée."
|
||||
notify_user_when_child_age_will_be_18:
|
||||
subject: "Votre enfant va avoir 18ans"
|
||||
body:
|
||||
child_age_will_be_18_years_ago: "Votre enfant %{NAME} va avoir 18ans, le %{DATE}, date à laquelle il sera automatiquement détaché de votre compte Famille. Il devra se créer son propre compte pour effectuer ses réservations."
|
||||
|
@ -126,6 +126,7 @@ Rails.application.routes.draw do
|
||||
resources :slots_reservations, only: [:update] do
|
||||
put 'cancel', on: :member
|
||||
put 'validate', on: :member
|
||||
put 'invalidate', on: :member
|
||||
end
|
||||
|
||||
resources :events do
|
||||
|
@ -62,4 +62,9 @@ auto_cancel_authorizations:
|
||||
class: TrainingAuthorizationWorker
|
||||
queue: default
|
||||
|
||||
child_age_will_be_18:
|
||||
cron: "0 0 0 * * *" # every day, at midnight
|
||||
class: ChildAgeWorker
|
||||
queue: default
|
||||
|
||||
<%= PluginRegistry.insert_code('yml.schedule') %>
|
||||
|
@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# add is_valid to slots_reservations
|
||||
# remove validated_at from slots_reservations
|
||||
class AddIsValidToSlotsReservations < ActiveRecord::Migration[7.0]
|
||||
def up
|
||||
add_column :slots_reservations, :is_valid, :boolean
|
||||
SlotsReservation.reset_column_information
|
||||
SlotsReservation.all.each do |sr|
|
||||
sr.update_column(:is_valid, true) if sr.validated_at.present?
|
||||
end
|
||||
remove_column :slots_reservations, :validated_at
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :slots_reservations, :is_valid
|
||||
add_column :slots_reservations, :validated_at, :datetime
|
||||
end
|
||||
end
|
@ -92,7 +92,10 @@ NOTIFICATIONS_TYPES = [
|
||||
{ name: 'notify_member_reservation_validated', category: 'agenda', is_configurable: false },
|
||||
{ name: 'notify_admin_reservation_validated', category: 'agenda', is_configurable: true },
|
||||
{ name: 'notify_member_pre_booked_reservation', category: 'agenda', is_configurable: false },
|
||||
{ name: 'notify_admin_member_pre_booked_reservation', category: 'agenda', is_configurable: true }
|
||||
{ name: 'notify_admin_member_pre_booked_reservation', category: 'agenda', is_configurable: true },
|
||||
{ name: 'notify_member_reservation_invalidated', category: 'agenda', is_configurable: false },
|
||||
{ name: 'notify_admin_reservation_invalidated', category: 'agenda', is_configurable: true },
|
||||
{ name: 'notify_user_when_child_age_will_be_18', category: 'users_accounts', is_configurable: false },
|
||||
].freeze
|
||||
|
||||
NOTIFICATIONS_TYPES.each do |notification_type|
|
||||
|
@ -9,6 +9,13 @@ SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
--
|
||||
-- Name: public; Type: SCHEMA; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
-- *not* creating schema, since initdb creates it
|
||||
|
||||
|
||||
--
|
||||
-- Name: fuzzystrmatch; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
@ -3273,7 +3280,7 @@ CREATE TABLE public.slots_reservations (
|
||||
ex_end_at timestamp without time zone,
|
||||
canceled_at timestamp without time zone,
|
||||
offered boolean DEFAULT false,
|
||||
validated_at timestamp(6) without time zone
|
||||
is_valid boolean
|
||||
);
|
||||
|
||||
|
||||
@ -9064,9 +9071,10 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20230524083558'),
|
||||
('20230524110215'),
|
||||
('20230525101006'),
|
||||
('20230612123250');
|
||||
('20230626103314');
|
||||
('20230612123250'),
|
||||
('20230626103314'),
|
||||
('20230626122844'),
|
||||
('20230626122947');
|
||||
('20230626122947'),
|
||||
('20230710072403');
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user