From a8dedfe040c8a0d7a8788132984cba41249d507e Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 6 Jul 2023 11:24:50 +0200 Subject: [PATCH 01/11] (ui) Add radio buttons for event validation --- .../templates/admin/events/reservations.html | 38 ++++++++++++------- config/locales/app.admin.en.yml | 4 +- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/app/frontend/templates/admin/events/reservations.html b/app/frontend/templates/admin/events/reservations.html index b2dad5e8d..64c93d542 100644 --- a/app/frontend/templates/admin/events/reservations.html +++ b/app/frontend/templates/admin/events/reservations.html @@ -20,13 +20,13 @@ - - - - - - - + + + + + + + @@ -52,12 +52,24 @@ {{ 'app.admin.event_reservations.event_status.canceled' }} diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index 27bb5ca10..4c872a5b2 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -668,6 +668,10 @@ en: 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." events_settings: title: "Settings" generic_text_block: "Editorial text block" diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index 337b37dd5..afaa99d5d 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -668,6 +668,10 @@ fr: 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." events_settings: title: "Paramètres" generic_text_block: "Bloc de texte rédactionnel" From 8b4150a8889ec8121f7cb2742aaa86506b9006ef Mon Sep 17 00:00:00 2001 From: Du Peng Date: Tue, 11 Jul 2023 14:17:03 +0200 Subject: [PATCH 04/11] (feat) show age of child in event reservation --- app/frontend/templates/admin/events/reservations.html | 1 + app/views/api/reservations/_reservation.json.jbuilder | 1 + config/locales/app.admin.en.yml | 1 + config/locales/app.admin.fr.yml | 1 + 4 files changed, 4 insertions(+) diff --git a/app/frontend/templates/admin/events/reservations.html b/app/frontend/templates/admin/events/reservations.html index f9e4026ec..acea1a3e8 100644 --- a/app/frontend/templates/admin/events/reservations.html +++ b/app/frontend/templates/admin/events/reservations.html @@ -38,6 +38,7 @@ {{ reservation.user_full_name }}
{{bu.name}} + ({{ 'app.admin.event_reservations.age' | translate:{NUMBER: bu.age} }})
diff --git a/app/views/api/reservations/_reservation.json.jbuilder b/app/views/api/reservations/_reservation.json.jbuilder index 478ba4ce7..b8803ed8b 100644 --- a/app/views/api/reservations/_reservation.json.jbuilder +++ b/app/views/api/reservations/_reservation.json.jbuilder @@ -40,6 +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? diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index 4c872a5b2..f0472b6b4 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -672,6 +672,7 @@ en: 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" diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index afaa99d5d..18ade1076 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -672,6 +672,7 @@ fr: 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" From 61fbb37fb254da544cdb1a17d0e7b9ff0d4ba9b0 Mon Sep 17 00:00:00 2001 From: Du Peng Date: Tue, 11 Jul 2023 14:57:38 +0200 Subject: [PATCH 05/11] (feat) add invalidate pre registration notification --- app/services/slots_reservations_service.rb | 6 ++++++ ..._notify_admin_reservation_invalidated.json.jbuilder | 4 ++++ ...notify_member_reservation_invalidated.json.jbuilder | 3 +++ .../notify_admin_reservation_invalidated.html.erb | 7 +++++++ .../notify_member_reservation_invalidated.html.erb | 3 +++ config/locales/en.yml | 4 ++++ config/locales/fr.yml | 4 ++++ config/locales/mails.en.yml | 10 +++++++++- config/locales/mails.fr.yml | 8 ++++++++ db/seeds/notification_types.rb | 4 +++- 10 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 app/views/api/notifications/_notify_admin_reservation_invalidated.json.jbuilder create mode 100644 app/views/api/notifications/_notify_member_reservation_invalidated.json.jbuilder create mode 100644 app/views/notifications_mailer/notify_admin_reservation_invalidated.html.erb create mode 100644 app/views/notifications_mailer/notify_member_reservation_invalidated.html.erb diff --git a/app/services/slots_reservations_service.rb b/app/services/slots_reservations_service.rb index a0d53bdfa..28f4210a6 100644 --- a/app/services/slots_reservations_service.rb +++ b/app/services/slots_reservations_service.rb @@ -47,6 +47,12 @@ class SlotsReservationsService 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 diff --git a/app/views/api/notifications/_notify_admin_reservation_invalidated.json.jbuilder b/app/views/api/notifications/_notify_admin_reservation_invalidated.json.jbuilder new file mode 100644 index 000000000..3e2aeefd4 --- /dev/null +++ b/app/views/api/notifications/_notify_admin_reservation_invalidated.json.jbuilder @@ -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')) diff --git a/app/views/api/notifications/_notify_member_reservation_invalidated.json.jbuilder b/app/views/api/notifications/_notify_member_reservation_invalidated.json.jbuilder new file mode 100644 index 000000000..7d223ed64 --- /dev/null +++ b/app/views/api/notifications/_notify_member_reservation_invalidated.json.jbuilder @@ -0,0 +1,3 @@ +json.title notification.notification_type +json.description t('.your_reservation_RESERVABLE_was_invalidated_html', + RESERVABLE: notification.attached_object.reservable.name) diff --git a/app/views/notifications_mailer/notify_admin_reservation_invalidated.html.erb b/app/views/notifications_mailer/notify_admin_reservation_invalidated.html.erb new file mode 100644 index 000000000..461e9235d --- /dev/null +++ b/app/views/notifications_mailer/notify_admin_reservation_invalidated.html.erb @@ -0,0 +1,7 @@ +<%= render 'notifications_mailer/shared/hello', recipient: @recipient %> + +

+ <%= t('.body.reservation_invalidated_html', + NAME: @attached_object.user&.profile&.full_name || t('api.notifications.deleted_user'), + RESERVABLE: @attached_object.reservable.name) %> +

diff --git a/app/views/notifications_mailer/notify_member_reservation_invalidated.html.erb b/app/views/notifications_mailer/notify_member_reservation_invalidated.html.erb new file mode 100644 index 000000000..caa17bc2c --- /dev/null +++ b/app/views/notifications_mailer/notify_member_reservation_invalidated.html.erb @@ -0,0 +1,3 @@ +<%= render 'notifications_mailer/shared/hello', recipient: @recipient %> + +

<%= t('.body.reservation_invalidated_html', RESERVATION: @attached_object.reservable.name) %>

diff --git a/config/locales/en.yml b/config/locales/en.yml index 30103b6f5..3673dd092 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -477,6 +477,10 @@ en: your_reservation_RESERVABLE_was_validated_html: "Your reservation %{RESERVABLE} was successfully validated." notify_admin_reservation_validated: a_RESERVABLE_reservation_was_validated_html: "A %{RESERVABLE} reservation of %{USER} was validated." + notify_member_reservation_invalidated: + your_reservation_RESERVABLE_was_invalidated_html: "Your pre-registration of %{RESERVABLE} wasn't validated." + notify_admin_reservation_invalidated: + a_RESERVABLE_reservation_was_invalidated_html: "A %{RESERVABLE} pre-registration of %{USER} was invalidated." #statistics tools for admins statistics: subscriptions: "Subscriptions" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 22f7210fa..ff6506e9f 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -477,6 +477,10 @@ fr: your_reservation_RESERVABLE_was_validated_html: "Votre réservation de %{RESERVABLE} a été validée." notify_admin_reservation_validated: a_RESERVABLE_reservation_was_validated_html: "La réservation de %{RESERVABLE} de %{NAME} a été validée." + notify_member_reservation_invalidated: + your_reservation_RESERVABLE_was_invalidated_html: "Votre demande de pré-inscription de %{RESERVABLE} n'a pas été validée." + notify_admin_reservation_invalidated: + a_RESERVABLE_reservation_was_invalidated_html: "La réservation de %{RESERVABLE} de %{NAME} a été invalidée." #statistics tools for admins statistics: subscriptions: "Abonnements" diff --git a/config/locales/mails.en.yml b/config/locales/mails.en.yml index 3cefad918..c3c927dc9 100644 --- a/config/locales/mails.en.yml +++ b/config/locales/mails.en.yml @@ -467,7 +467,15 @@ en: reservation_validated_html: "%{RESERVABLE} 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: "%{RESERVABLE} of %{NAME} was validated." reserved_slots: "Reserved slots are:" + notify_member_reservation_invalidated: + subject: "Your pre-registration wasn't validated" + body: + reservation_invalidated_html: "%{RESERVABLE} wasn't validated." + notify_admin_reservation_invalidated: + subject: "Pre-registration wasn't validated" + body: + reservation_invalidated_html: "%{RESERVABLE} of %{NAME} wasn't validated." diff --git a/config/locales/mails.fr.yml b/config/locales/mails.fr.yml index b874511c3..232c48669 100644 --- a/config/locales/mails.fr.yml +++ b/config/locales/mails.fr.yml @@ -471,3 +471,11 @@ fr: body: reservation_validated_html: "%{RESERVABLE} 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 %{RESERVATION} 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: "%{RESERVABLE} du membre %{NAME} n'a pas été validée." diff --git a/db/seeds/notification_types.rb b/db/seeds/notification_types.rb index 9cce6a6a5..1f768bb60 100644 --- a/db/seeds/notification_types.rb +++ b/db/seeds/notification_types.rb @@ -92,7 +92,9 @@ 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 } ].freeze NOTIFICATIONS_TYPES.each do |notification_type| From e43d4918e12d86494e248166af9eb2fe2c1a9275 Mon Sep 17 00:00:00 2001 From: Du Peng Date: Tue, 11 Jul 2023 15:42:46 +0200 Subject: [PATCH 06/11] (feat) show present status for pre registration evnet if amount == 0 --- app/frontend/templates/admin/events/reservations.html | 3 ++- config/locales/app.admin.en.yml | 1 + config/locales/app.admin.fr.yml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/frontend/templates/admin/events/reservations.html b/app/frontend/templates/admin/events/reservations.html index acea1a3e8..44f7392ef 100644 --- a/app/frontend/templates/admin/events/reservations.html +++ b/app/frontend/templates/admin/events/reservations.html @@ -49,7 +49,8 @@
{{ 'app.admin.event_reservations.booked_by' }}{{ 'app.admin.event_reservations.reservations' }}{{ 'app.admin.event_reservations.date' }}{{ 'app.admin.event_reservations.reserved_tickets' }}{{ 'app.admin.event_reservations.status' }}{{ 'app.admin.event_reservations.gestion' }}{{ 'app.admin.event_reservations.booked_by' }}{{ 'app.admin.event_reservations.reservations' }}{{ 'app.admin.event_reservations.date' }}{{ 'app.admin.event_reservations.reserved_tickets' }}{{ 'app.admin.event_reservations.status' }}{{ 'app.admin.event_reservations.validation' }}
- - +
+
+ + +
+ + +
diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index eec65bd06..9fa022844 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -646,12 +646,14 @@ en: booked_by: "Booked by" reservations: "Reservations" status: "Status" - gestion: "Gestion" + validation: "Validation" event_status: pre_registered: "Pre-registered" to_pay: "To pay" paid: "Paid" canceled: "Canceled" + affirmative: "yes" + negative: "no" validate: "Validate" pay: "Pay" validate_the_reservation: "Validate the reservation" From d271fa59e662decddc4aad2eb6b8dfcc4a9c760a Mon Sep 17 00:00:00 2001 From: Du Peng Date: Tue, 11 Jul 2023 10:36:44 +0200 Subject: [PATCH 02/11] (feat) valid/invalid pre registration event --- .../api/slots_reservations_controller.rb | 7 ++- .../events/event-reservation-item.tsx | 4 +- .../javascript/controllers/admin/events.js | 45 ++++++++++--------- .../src/javascript/controllers/events.js.erb | 4 +- .../src/javascript/models/reservation.ts | 3 +- .../javascript/services/slots_reservation.js | 4 ++ .../templates/admin/events/reservations.html | 9 ++-- app/frontend/templates/events/show.html | 4 +- app/models/event.rb | 4 +- app/policies/slots_reservation_policy.rb | 4 ++ app/services/slots_reservations_service.rb | 14 +++++- app/views/api/members/show.json.jbuilder | 2 +- .../reservations/_reservation.json.jbuilder | 3 +- config/locales/app.admin.en.yml | 3 ++ config/locales/app.admin.fr.yml | 5 +++ config/routes.rb | 1 + ...2403_add_is_valid_to_slots_reservations.rb | 19 ++++++++ db/structure.sql | 24 ++++++---- 18 files changed, 112 insertions(+), 47 deletions(-) create mode 100644 db/migrate/20230710072403_add_is_valid_to_slots_reservations.rb diff --git a/app/controllers/api/slots_reservations_controller.rb b/app/controllers/api/slots_reservations_controller.rb index c0f002f94..00a355735 100644 --- a/app/controllers/api/slots_reservations_controller.rb +++ b/app/controllers/api/slots_reservations_controller.rb @@ -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 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 d9cd2ca4c..12b619fae 100644 --- a/app/frontend/src/javascript/components/events/event-reservation-item.tsx +++ b/app/frontend/src/javascript/components/events/event-reservation-item.tsx @@ -44,9 +44,9 @@ export const EventReservationItem: React.FC = ({ 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'); diff --git a/app/frontend/src/javascript/controllers/admin/events.js b/app/frontend/src/javascript/controllers/admin/events.js index ebc30999b..87cade06b 100644 --- a/app/frontend/src/javascript/controllers/admin/events.js +++ b/app/frontend/src/javascript/controllers/admin/events.js @@ -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')); }); }; diff --git a/app/frontend/src/javascript/controllers/events.js.erb b/app/frontend/src/javascript/controllers/events.js.erb index 3fbcd3fdf..c08a98530 100644 --- a/app/frontend/src/javascript/controllers/events.js.erb +++ b/app/frontend/src/javascript/controllers/events.js.erb @@ -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.is_valid) { return false; } } @@ -748,7 +748,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' name: child.first_name + ' ' + child.last_name, id: child.id, type: 'Child', - validatedAt: child.validated_at, + is_valid: child.is_valid, birthday: child.birthday }); } diff --git a/app/frontend/src/javascript/models/reservation.ts b/app/frontend/src/javascript/models/reservation.ts index f5abfe7b9..7f2d7d415 100644 --- a/app/frontend/src/javascript/models/reservation.ts +++ b/app/frontend/src/javascript/models/reservation.ts @@ -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, } diff --git a/app/frontend/src/javascript/services/slots_reservation.js b/app/frontend/src/javascript/services/slots_reservation.js index ed833cbb5..c6d59c9cf 100644 --- a/app/frontend/src/javascript/services/slots_reservation.js +++ b/app/frontend/src/javascript/services/slots_reservation.js @@ -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' } } ); diff --git a/app/frontend/templates/admin/events/reservations.html b/app/frontend/templates/admin/events/reservations.html index 64c93d542..5d65e4310 100644 --- a/app/frontend/templates/admin/events/reservations.html +++ b/app/frontend/templates/admin/events/reservations.html @@ -53,19 +53,16 @@
-
+
- diff --git a/app/frontend/templates/events/show.html b/app/frontend/templates/events/show.html index 81827e975..045e35deb 100644 --- a/app/frontend/templates/events/show.html +++ b/app/frontend/templates/events/show.html @@ -142,7 +142,7 @@ class="form-control"> - + {{ 'app.shared.cart.child_validation_required_alert' }} @@ -180,7 +180,7 @@ class="form-control"> - +

{{ 'app.shared.cart.child_validation_required_alert' }} diff --git a/app/models/event.rb b/app/models/event.rb index c91011998..af039ca81 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -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 diff --git a/app/policies/slots_reservation_policy.rb b/app/policies/slots_reservation_policy.rb index 4490d5316..8774a770f 100644 --- a/app/policies/slots_reservation_policy.rb +++ b/app/policies/slots_reservation_policy.rb @@ -19,4 +19,8 @@ class SlotsReservationPolicy < ApplicationPolicy def validate? user.admin? || user.manager? end + + def invalidate? + user.admin? || user.manager? + end end diff --git a/app/services/slots_reservations_service.rb b/app/services/slots_reservations_service.rb index 668868ba1..a0d53bdfa 100644 --- a/app/services/slots_reservations_service.rb +++ b/app/services/slots_reservations_service.rb @@ -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,17 @@ 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 + return true + end + false + end end end diff --git a/app/views/api/members/show.json.jbuilder b/app/views/api/members/show.json.jbuilder index 28bdadf3f..0ff25f4da 100644 --- a/app/views/api/members/show.json.jbuilder +++ b/app/views/api/members/show.json.jbuilder @@ -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| diff --git a/app/views/api/reservations/_reservation.json.jbuilder b/app/views/api/reservations/_reservation.json.jbuilder index 8e91cc028..478ba4ce7 100644 --- a/app/views/api/reservations/_reservation.json.jbuilder +++ b/app/views/api/reservations/_reservation.json.jbuilder @@ -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 @@ -41,4 +41,5 @@ json.booking_users_attributes reservation.booking_users.order(booked_type: :desc json.booked_id bu.booked_id json.booked_type bu.booked_type end +json.is_valid reservation.slots_reservations[0].is_valid json.is_paid reservation.invoice_items.count.positive? diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index 9fa022844..27bb5ca10 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -646,6 +646,7 @@ en: booked_by: "Booked by" reservations: "Reservations" status: "Status" + gestion: "Gestion" validation: "Validation" event_status: pre_registered: "Pre-registered" @@ -660,6 +661,8 @@ en: 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" diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index 48d19dbbb..337b37dd5 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -647,17 +647,22 @@ fr: reservations: "Réservations" status: "Statut" gestion: "Gestion" + validation: "Validation" event_status: pre_registered: "Pré-inscrit" to_pay: "À payer" paid: "Payé" canceled: "Annulée" + 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" diff --git a/config/routes.rb b/config/routes.rb index 3e9192499..ea732b1cd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 diff --git a/db/migrate/20230710072403_add_is_valid_to_slots_reservations.rb b/db/migrate/20230710072403_add_is_valid_to_slots_reservations.rb new file mode 100644 index 000000000..64f851e4a --- /dev/null +++ b/db/migrate/20230710072403_add_is_valid_to_slots_reservations.rb @@ -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 diff --git a/db/structure.sql b/db/structure.sql index 820ccb267..9d16cfde4 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -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 ); @@ -9056,17 +9063,18 @@ INSERT INTO "schema_migrations" (version) VALUES ('20230328094809'), ('20230331132506'), ('20230509121907'), -('20230509161557'); -('20230510141305'); +('20230509161557'), +('20230510141305'), ('20230511080650'), -('20230511081018'); +('20230511081018'), ('20230524080448'), ('20230524083558'), -('20230524110215'); +('20230524110215'), ('20230525101006'), +('20230612123250'), +('20230626103314'), ('20230626122844'), -('20230626122947'); -('20230612123250'); -('20230626103314'); +('20230626122947'), +('20230710072403'); From 8771eb8cccd995ff1bdd81856af3516a09b27679 Mon Sep 17 00:00:00 2001 From: Du Peng Date: Tue, 11 Jul 2023 13:53:41 +0200 Subject: [PATCH 03/11] (feat) admin can confirm the prensence of pre-registration event --- .../javascript/controllers/admin/events.js | 15 +++- .../admin/events/pay_reservation_modal.html | 69 ++++++++++--------- .../templates/admin/events/reservations.html | 5 +- config/locales/app.admin.en.yml | 4 ++ config/locales/app.admin.fr.yml | 4 ++ 5 files changed, 61 insertions(+), 36 deletions(-) diff --git a/app/frontend/src/javascript/controllers/admin/events.js b/app/frontend/src/javascript/controllers/admin/events.js index 87cade06b..d1288f4f2 100644 --- a/app/frontend/src/javascript/controllers/admin/events.js +++ b/app/frontend/src/javascript/controllers/admin/events.js @@ -516,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; }, @@ -529,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; @@ -614,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 () { diff --git a/app/frontend/templates/admin/events/pay_reservation_modal.html b/app/frontend/templates/admin/events/pay_reservation_modal.html index 766d1e68f..c1866fe17 100644 --- a/app/frontend/templates/admin/events/pay_reservation_modal.html +++ b/app/frontend/templates/admin/events/pay_reservation_modal.html @@ -1,45 +1,52 @@

{{ reservation.created_at | amDateFormat:'LL LTS' }} {{ 'app.admin.event_reservations.event_status.pre_registered' }} {{ 'app.admin.event_reservations.event_status.to_pay' }} - {{ 'app.admin.event_reservations.event_status.paid' }} + {{ 'app.admin.event_reservations.event_status.paid' }} + {{ 'app.admin.event_reservations.event_status.present' }} {{ 'app.admin.event_reservations.event_status.canceled' }} diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index f0472b6b4..63d082b5e 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -653,6 +653,7 @@ en: to_pay: "To pay" paid: "Paid" canceled: "Canceled" + present: "Present" affirmative: "yes" negative: "no" validate: "Validate" diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index 18ade1076..709e68ae4 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -653,6 +653,7 @@ fr: to_pay: "À payer" paid: "Payé" canceled: "Annulée" + present: "Présent" affirmative: "Oui" negative: "Non" validate: "Valider" From 770d1edbe6336605b766c9c822b820bbcd3bf7e7 Mon Sep 17 00:00:00 2001 From: Du Peng Date: Tue, 11 Jul 2023 15:57:47 +0200 Subject: [PATCH 07/11] (i18n) update translations --- config/locales/app.public.fr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/app.public.fr.yml b/config/locales/app.public.fr.yml index 630f720cb..dfa1e53dc 100644 --- a/config/locales/app.public.fr.yml +++ b/config/locales/app.public.fr.yml @@ -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}) :" From 9dc85ee80bb4f1327358d1d35157a05d9e2c8e4a Mon Sep 17 00:00:00 2001 From: Du Peng Date: Tue, 11 Jul 2023 16:07:37 +0200 Subject: [PATCH 08/11] (bug) cannot create event --- app/frontend/src/javascript/components/events/event-form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/frontend/src/javascript/components/events/event-form.tsx b/app/frontend/src/javascript/components/events/event-form.tsx index 209f17553..1013bb0b2 100644 --- a/app/frontend/src/javascript/components/events/event-form.tsx +++ b/app/frontend/src/javascript/components/events/event-form.tsx @@ -100,7 +100,7 @@ export const EventForm: React.FC = ({ action, event, onError, on * Callback triggered when the user validates the machine form: handle create or update */ const onSubmit: SubmitHandler = (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') { From 67f5e68fd718ea8ea3d82b6585ebf61c1103d682 Mon Sep 17 00:00:00 2001 From: Du Peng Date: Tue, 11 Jul 2023 19:13:38 +0200 Subject: [PATCH 09/11] (feat) user cannot reserve 2 times for event family --- .../src/javascript/controllers/events.js.erb | 34 +++++++++++++------ app/frontend/templates/events/show.html | 2 +- app/models/child.rb | 2 +- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/app/frontend/src/javascript/controllers/events.js.erb b/app/frontend/src/javascript/controllers/events.js.erb index c08a98530..eceaacc9f 100644 --- a/app/frontend/src/javascript/controllers/events.js.erb +++ b/app/frontend/src/javascript/controllers/events.js.erb @@ -644,7 +644,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' if (!user.booked) { return false; } - if ($scope.enableChildValidationRequired && user.booked.type === 'Child' && !user.booked.is_valid) { + 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', - is_valid: child.is_valid, + 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; } diff --git a/app/frontend/templates/events/show.html b/app/frontend/templates/events/show.html index 045e35deb..0f0183e5b 100644 --- a/app/frontend/templates/events/show.html +++ b/app/frontend/templates/events/show.html @@ -142,7 +142,7 @@ class="form-control"> - + {{ 'app.shared.cart.child_validation_required_alert' }} diff --git a/app/models/child.rb b/app/models/child.rb index 3d752ac54..e91ff6b83 100644 --- a/app/models/child.rb +++ b/app/models/child.rb @@ -11,7 +11,7 @@ class Child < ApplicationRecord validates :first_name, presence: true validates :last_name, presence: true # validates :email, presence: true, format: { with: Devise.email_regexp } - validate :validate_age + # validate :validate_age # birthday should less than 18 years ago def validate_age From 03d71d7a84a4ac6e63477d564141d5b388d8f239 Mon Sep 17 00:00:00 2001 From: Du Peng Date: Wed, 12 Jul 2023 08:44:02 +0200 Subject: [PATCH 10/11] (feat) hide child birthday > 18 years ago --- app/controllers/api/children_controller.rb | 2 +- app/models/child.rb | 2 +- app/views/api/members/list.json.jbuilder | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/children_controller.rb b/app/controllers/api/children_controller.rb index ac1d98b78..44188807e 100644 --- a/app/controllers/api/children_controller.rb +++ b/app/controllers/api/children_controller.rb @@ -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 diff --git a/app/models/child.rb b/app/models/child.rb index e91ff6b83..3d752ac54 100644 --- a/app/models/child.rb +++ b/app/models/child.rb @@ -11,7 +11,7 @@ class Child < ApplicationRecord validates :first_name, presence: true validates :last_name, presence: true # validates :email, presence: true, format: { with: Devise.email_regexp } - # validate :validate_age + validate :validate_age # birthday should less than 18 years ago def validate_age diff --git a/app/views/api/members/list.json.jbuilder b/app/views/api/members/list.json.jbuilder index 97840a4b8..764939ce7 100644 --- a/app/views/api/members/list.json.jbuilder +++ b/app/views/api/members/list.json.jbuilder @@ -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 From b6f2187d942745dce83fa60981a237ce7e39e24d Mon Sep 17 00:00:00 2001 From: Du Peng Date: Wed, 12 Jul 2023 09:29:16 +0200 Subject: [PATCH 11/11] (feat) add child age will be 18 worker --- ...y_user_when_child_age_will_be_18.json.jbuilder | 6 ++++++ ...notify_user_when_child_age_will_be_18.html.erb | 6 ++++++ app/workers/child_age_worker.rb | 15 +++++++++++++++ config/locales/en.yml | 2 ++ config/locales/fr.yml | 2 ++ config/locales/mails.en.yml | 4 ++++ config/locales/mails.fr.yml | 4 ++++ config/schedule.yml | 5 +++++ db/seeds/notification_types.rb | 3 ++- 9 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 app/views/api/notifications/_notify_user_when_child_age_will_be_18.json.jbuilder create mode 100644 app/views/notifications_mailer/notify_user_when_child_age_will_be_18.html.erb create mode 100644 app/workers/child_age_worker.rb diff --git a/app/views/api/notifications/_notify_user_when_child_age_will_be_18.json.jbuilder b/app/views/api/notifications/_notify_user_when_child_age_will_be_18.json.jbuilder new file mode 100644 index 000000000..348b47f06 --- /dev/null +++ b/app/views/api/notifications/_notify_user_when_child_age_will_be_18.json.jbuilder @@ -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)) diff --git a/app/views/notifications_mailer/notify_user_when_child_age_will_be_18.html.erb b/app/views/notifications_mailer/notify_user_when_child_age_will_be_18.html.erb new file mode 100644 index 000000000..cb845ff68 --- /dev/null +++ b/app/views/notifications_mailer/notify_user_when_child_age_will_be_18.html.erb @@ -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), +}) %> diff --git a/app/workers/child_age_worker.rb b/app/workers/child_age_worker.rb new file mode 100644 index 000000000..374b451bf --- /dev/null +++ b/app/workers/child_age_worker.rb @@ -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 diff --git a/config/locales/en.yml b/config/locales/en.yml index 3673dd092..92c110377 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -481,6 +481,8 @@ en: your_reservation_RESERVABLE_was_invalidated_html: "Your pre-registration of %{RESERVABLE} wasn't validated." notify_admin_reservation_invalidated: a_RESERVABLE_reservation_was_invalidated_html: "A %{RESERVABLE} pre-registration of %{USER} 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" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index ff6506e9f..d94ecc30f 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -481,6 +481,8 @@ fr: your_reservation_RESERVABLE_was_invalidated_html: "Votre demande de pré-inscription de %{RESERVABLE} n'a pas été validée." notify_admin_reservation_invalidated: a_RESERVABLE_reservation_was_invalidated_html: "La réservation de %{RESERVABLE} de %{NAME} 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" diff --git a/config/locales/mails.en.yml b/config/locales/mails.en.yml index c3c927dc9..20f6bc39d 100644 --- a/config/locales/mails.en.yml +++ b/config/locales/mails.en.yml @@ -479,3 +479,7 @@ en: subject: "Pre-registration wasn't validated" body: reservation_invalidated_html: "%{RESERVABLE} 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." diff --git a/config/locales/mails.fr.yml b/config/locales/mails.fr.yml index 232c48669..a6cda1010 100644 --- a/config/locales/mails.fr.yml +++ b/config/locales/mails.fr.yml @@ -479,3 +479,7 @@ fr: subject: "Demande of pré-inscription n'a pas été validée" body: reservation_invalidated_html: "%{RESERVABLE} 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." diff --git a/config/schedule.yml b/config/schedule.yml index 31e0c7f81..93b45c9e9 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -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') %> diff --git a/db/seeds/notification_types.rb b/db/seeds/notification_types.rb index 1f768bb60..582b4a030 100644 --- a/db/seeds/notification_types.rb +++ b/db/seeds/notification_types.rb @@ -94,7 +94,8 @@ NOTIFICATIONS_TYPES = [ { 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_member_reservation_invalidated', category: 'agenda', is_configurable: false }, - { name: 'notify_admin_reservation_invalidated', category: 'agenda', is_configurable: true } + { 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|