diff --git a/app/controllers/api/children_controller.rb b/app/controllers/api/children_controller.rb index 44188807e..3725695ba 100644 --- a/app/controllers/api/children_controller.rb +++ b/app/controllers/api/children_controller.rb @@ -30,7 +30,7 @@ class API::ChildrenController < API::APIController def update authorize @child - if @child.update(child_params) + if ChildService.update(@child, child_params) render status: :ok else render json: @child.errors.full_messages, status: :unprocessable_entity diff --git a/app/frontend/src/javascript/components/events/event-reservation-item.tsx b/app/frontend/src/javascript/components/events/event-reservation-item.tsx index b186fd508..7075c367d 100644 --- a/app/frontend/src/javascript/components/events/event-reservation-item.tsx +++ b/app/frontend/src/javascript/components/events/event-reservation-item.tsx @@ -47,15 +47,15 @@ export const EventReservationItem: React.FC = ({ rese const preRegistrationStatus = () => { if (!_.isBoolean(reservation.is_valid) && !reservation.canceled_at && !reservation.is_paid) { return t('app.logged.event_reservation_item.in_the_process_of_validation'); - } else if (reservation.is_valid && !reservation.canceled_at && !reservation.is_paid && reservation.reservable.amount !== 0) { + } else if (reservation.is_valid && !reservation.canceled_at && !reservation.is_paid && reservation.amount !== 0) { return t('app.logged.event_reservation_item.settle_your_payment'); - } else if (reservation.is_valid && !reservation.canceled_at && !reservation.is_paid && reservation.reservable.amount === 0) { + } else if (reservation.is_valid && !reservation.canceled_at && !reservation.is_paid && reservation.amount === 0) { return t('app.logged.event_reservation_item.registered'); } else if (!reservation.is_valid && !reservation.canceled_at) { return t('app.logged.event_reservation_item.not_validated'); - } else if (reservation.is_paid && !reservation.canceled_at && reservation.reservable.amount !== 0) { + } else if (reservation.is_paid && !reservation.canceled_at && reservation.amount !== 0) { return t('app.logged.event_reservation_item.paid'); - } else if (reservation.is_paid && !reservation.canceled_at && reservation.reservable.amount === 0) { + } else if (reservation.is_paid && !reservation.canceled_at && reservation.amount === 0) { return t('app.logged.event_reservation_item.present'); } else if (reservation.canceled_at) { return t('app.logged.event_reservation_item.canceled'); diff --git a/app/frontend/src/javascript/components/family-account/child-form.tsx b/app/frontend/src/javascript/components/family-account/child-form.tsx index f9e9b4a53..00887017f 100644 --- a/app/frontend/src/javascript/components/family-account/child-form.tsx +++ b/app/frontend/src/javascript/components/family-account/child-form.tsx @@ -108,7 +108,7 @@ export const ChildForm: React.FC = ({ child, onSubmit, supportin label={t('app.public.child_form.email')} /> - {!isPrivileged() && <> + {!isPrivileged() && supportingDocumentsTypes?.length > 0 && <>

{t('app.public.child_form.supporting_documents')}

{output.supporting_document_files_attributes.map((sf, index) => { return ( @@ -116,7 +116,7 @@ export const ChildForm: React.FC = ({ child, onSubmit, supportin defaultFile={sf as FileType} id={`supporting_document_files_attributes.${index}`} accept="application/pdf" - rules={{ required: true }} + rules={{ required: !sf.attachment }} setValue={setValue} label={getSupportingDocumentsTypeName(sf.supporting_document_type_id)} showRemoveButton={false} @@ -132,7 +132,7 @@ export const ChildForm: React.FC = ({ child, onSubmit, supportin - {isPrivileged() && <> + {isPrivileged() && supportingDocumentsTypes?.length > 0 && <>

{t('app.public.child_form.supporting_documents')}

{output.supporting_document_files_attributes.map((sf, index) => { @@ -158,7 +158,7 @@ export const ChildForm: React.FC = ({ child, onSubmit, supportin
} - {isPrivileged() && <> + {isPrivileged() && supportingDocumentsTypes?.length > 0 && <>

{t('app.public.child_form.refuse_documents_info')}

diff --git a/app/frontend/src/javascript/controllers/admin/events.js b/app/frontend/src/javascript/controllers/admin/events.js index 02b19a1d4..420097e01 100644 --- a/app/frontend/src/javascript/controllers/admin/events.js +++ b/app/frontend/src/javascript/controllers/admin/events.js @@ -461,6 +461,34 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope', return reservation.slots_reservations_attributes[0].is_valid === true || reservation.slots_reservations_attributes[0].is_valid === 'true'; }; + /** + * Test if the provided reservation has been invalidated + * @param reservation {Reservation} + * @returns {boolean} + */ + $scope.isInvalidated = function (reservation) { + return reservation.slots_reservations_attributes[0].is_valid === false || reservation.slots_reservations_attributes[0].is_valid === 'false'; + }; + + /** + * Get the price of a reservation + * @param reservation {Reservation} + */ + $scope.reservationAmount = function (reservation) { + let amount = 0; + for (const user of reservation.booking_users_attributes) { + if (user.event_price_category_id) { + const price_category = _.find($scope.event.event_price_categories_attributes, { id: user.event_price_category_id }); + if (price_category) { + amount += price_category.amount; + } + } else { + amount += $scope.event.amount; + } + } + return amount; + }; + /** * Callback to validate a reservation * @param reservation {Reservation} @@ -625,7 +653,7 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope', if (r.id === reservation.id) { return reservation; } - if ($scope.event.amount === 0) { + if ($scope.reservationAmount(reservation) === 0) { growl.success(_t('app.admin.event_reservations.reservation_was_successfully_present')); } else { growl.success(_t('app.admin.event_reservations.reservation_was_successfully_paid')); diff --git a/app/frontend/src/javascript/controllers/events.js.erb b/app/frontend/src/javascript/controllers/events.js.erb index 8117807a1..c55105338 100644 --- a/app/frontend/src/javascript/controllers/events.js.erb +++ b/app/frontend/src/javascript/controllers/events.js.erb @@ -236,8 +236,19 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' * Callback to call when the number of tickets to book changes in the current booking */ $scope.changeNbPlaces = function (priceType) { + let reservedPlaces = 0; + if ($scope.event.event_type === 'family') { + reservedPlaces = $scope.reservations.reduce((sum, reservation) => { + return sum + reservation.booking_users_attributes.length; + }, 0); + } + let nb_free_places = $scope.event.nb_free_places; + if ($scope.event.event_type === 'family') { + const maxPlaces = $scope.children.length + 1 - reservedPlaces; + nb_free_places = Math.min(maxPlaces, $scope.event.nb_free_places); + } // compute the total remaining places - let remain = ($scope.event.event_type === 'family' ? ($scope.children.length + 1) : $scope.event.nb_free_places) - $scope.reserve.nbReservePlaces; + let remain = nb_free_places - $scope.reserve.nbReservePlaces; for (let ticket in $scope.reserve.tickets) { remain -= $scope.reserve.tickets[ticket]; } @@ -360,8 +371,8 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' if ($scope.ctrl.member) { Member.get({ id: $scope.ctrl.member.id }, function (member) { $scope.ctrl.member = member; - getReservations($scope.event.id, 'Event', $scope.ctrl.member.id).then(function (reservations) { - getChildren($scope.currentUser.id).then(function (children) { + getReservations($scope.event.id, 'Event', $scope.ctrl.member.id).then(function () { + getChildren($scope.ctrl.member.id).then(function (children) { updateNbReservePlaces(); }); }); @@ -678,7 +689,7 @@ 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).then(function (reservations) { + getReservations($scope.event.id, 'Event', $scope.currentUser.id).then(function () { getChildren($scope.currentUser.id).then(function (children) { updateNbReservePlaces(); }); @@ -772,7 +783,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' * update number of places available for each price category for the family event */ const updateNbReservePlaces = function () { - if ($scope.event.event_type === 'family') { + if ($scope.event.event_type === 'family' && $scope.ctrl.member.id) { const reservedPlaces = $scope.reservations.reduce((sum, reservation) => { return sum + reservation.booking_users_attributes.length; }, 0); @@ -990,7 +1001,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' const afterPayment = function (invoice) { Event.get({ id: $scope.event.id }).$promise.then(function (event) { $scope.event = event; - getReservations($scope.event.id, 'Event', $scope.ctrl.member.id).then(function (reservations) { + getReservations($scope.event.id, 'Event', $scope.ctrl.member.id).then(function () { updateNbReservePlaces(); $scope.reserveSuccess = true; $scope.coupon.applied = null; diff --git a/app/frontend/src/javascript/models/reservation.ts b/app/frontend/src/javascript/models/reservation.ts index ee1d2293d..14529b2cb 100644 --- a/app/frontend/src/javascript/models/reservation.ts +++ b/app/frontend/src/javascript/models/reservation.ts @@ -70,10 +70,11 @@ export interface Reservation { end_at: TDateISO, event_type?: string, event_title?: string, - event_pre_registration?: boolean + event_pre_registration?: boolean, canceled_at?: TDateISO, is_valid?: boolean, is_paid?: boolean, + amount?: number } export interface ReservationIndexFilter extends ApiFilter { diff --git a/app/frontend/templates/admin/events/pay_reservation_modal.html b/app/frontend/templates/admin/events/pay_reservation_modal.html index c1866fe17..b4059d18f 100644 --- a/app/frontend/templates/admin/events/pay_reservation_modal.html +++ b/app/frontend/templates/admin/events/pay_reservation_modal.html @@ -1,10 +1,10 @@