From aaf36dcc0a9ffeafb0f6959f625bc91add98eec9 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Thu, 29 Apr 2021 16:29:35 +0200 Subject: [PATCH] reserve events w/ payzen we cannot use the directive because the layout is too much different --- .../components/payment/payzen/payzen-form.tsx | 4 +- .../javascript/controllers/admin/events.js | 33 +++-- .../src/javascript/controllers/events.js.erb | 139 +++++++----------- app/frontend/templates/events/show.html | 20 +-- .../templates/stripe/payment_modal.html | 55 ------- app/models/cart_item/event_reservation.rb | 2 +- .../_payment_schedule.json.jbuilder | 2 +- .../reservations/_reservation.json.jbuilder | 4 +- app/views/api/reservations/show.json.jbuilder | 4 +- 9 files changed, 86 insertions(+), 177 deletions(-) delete mode 100644 app/frontend/templates/stripe/payment_modal.html diff --git a/app/frontend/src/javascript/components/payment/payzen/payzen-form.tsx b/app/frontend/src/javascript/components/payment/payzen/payzen-form.tsx index a817ba6f4..5539c11ef 100644 --- a/app/frontend/src/javascript/components/payment/payzen/payzen-form.tsx +++ b/app/frontend/src/javascript/components/payment/payzen/payzen-form.tsx @@ -48,9 +48,9 @@ export const PayzenForm: React.FC = ({ onSubmit, onSuccess, on const transaction = event.clientAnswer.transactions[0]; if (event.clientAnswer.orderStatus === 'PAID') { - PayzenAPI.confirm(event.clientAnswer.orderDetails.orderId, cartItems).then(() => { + PayzenAPI.confirm(event.clientAnswer.orderDetails.orderId, cartItems).then((confirmation) => { PayZenKR.current.removeForms().then(() => { - onSuccess(event.clientAnswer); + onSuccess(confirmation); }); }) } else { diff --git a/app/frontend/src/javascript/controllers/admin/events.js b/app/frontend/src/javascript/controllers/admin/events.js index 2bf5635fc..1fa3d5800 100644 --- a/app/frontend/src/javascript/controllers/admin/events.js +++ b/app/frontend/src/javascript/controllers/admin/events.js @@ -284,7 +284,8 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state', resolve: { category () { return {}; } }, - controller: 'PriceCategoryController' }).result['finally'](null).then(function (p_cat) { + controller: 'PriceCategoryController' + }).result.finally(null).then(function (p_cat) { // save the price category to the API PriceCategory.save(p_cat, function (cat) { $scope.priceCategories.push(cat); @@ -312,7 +313,8 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state', resolve: { category () { return $scope.priceCategories[index]; } }, - controller: 'PriceCategoryController' }).result['finally'](null).then(function (p_cat) { + controller: 'PriceCategoryController' + }).result.finally(null).then(function (p_cat) { // update the price category to the API PriceCategory.update({ id }, { price_category: p_cat }, function (cat) { $scope.priceCategories[index] = cat; @@ -374,7 +376,6 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state', return $scope.page = 1; }; - /** * Setup the feature-tour for the admin/events page. * This is intended as a contextual help (when pressing F1) @@ -468,7 +469,7 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state', if (settingsPromise.feature_tour_display !== 'manual' && $scope.currentUser.profile.tours.indexOf('events') < 0) { uitour.start(); } - } + }; /* PRIVATE SCOPE */ @@ -532,9 +533,9 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope', * @param reservation {Reservation} * @returns {boolean} */ - $scope.isCancelled = function(reservation) { - return !!(reservation.slots[0].canceled_at); - } + $scope.isCancelled = function (reservation) { + return !!(reservation.slots_attributes[0].canceled_at); + }; }]); /** @@ -585,7 +586,7 @@ Application.Controllers.controller('NewEventController', ['$scope', '$state', 'C ]; // triggered when the new event form was submitted to the API and have received an answer - $scope.onSubmited = function(content) { + $scope.onSubmited = function (content) { if ((content.id == null)) { $scope.alerts = []; angular.forEach(content, function (v, k) { @@ -655,7 +656,7 @@ Application.Controllers.controller('EditEventController', ['$scope', '$state', ' } }); // submit form event by edit-mode - modalInstance.result.then(function(res) { + modalInstance.result.then(function (res) { $scope.isShowEditModeModal = false; $scope.editMode = res.editMode; e.target.click(); @@ -664,12 +665,12 @@ Application.Controllers.controller('EditEventController', ['$scope', '$state', ' }; // triggered when the edit event form was submitted to the API and have received an answer - $scope.onSubmited = function(data) { + $scope.onSubmited = function (data) { if (data.total === data.updated) { if (data.updated > 1) { growl.success(_t( 'app.admin.events_edit.events_updated', - {COUNT: data.updated - 1} + { COUNT: data.updated - 1 } )); } else { growl.success(_t( @@ -680,7 +681,7 @@ Application.Controllers.controller('EditEventController', ['$scope', '$state', ' if (data.total > 1) { growl.warning(_t( 'app.admin.events_edit.events_not_updated', - {TOTAL: data.total, COUNT: data.total - data.updated} + { TOTAL: data.total, COUNT: data.total - data.updated } )); if (_.find(data.details, { error: 'EventPriceCategory' })) { growl.error(_t( @@ -750,20 +751,20 @@ Application.Controllers.controller('EditRecurrentEventController', ['$scope', '$ $uibModalInstance.close({ editMode: $scope.editMode }); - } + }; /** * Test if any of the dates of the event has changed */ - $scope.hasDateChanged = function() { + $scope.hasDateChanged = function () { return (!moment(initialDates.start).isSame(currentEvent.start_date, 'day') || !moment(initialDates.end).isSame(currentEvent.end_date, 'day')); - } + }; /** * Cancellation callback */ $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); - } + }; } ]); diff --git a/app/frontend/src/javascript/controllers/events.js.erb b/app/frontend/src/javascript/controllers/events.js.erb index 03500d7da..489563da7 100644 --- a/app/frontend/src/javascript/controllers/events.js.erb +++ b/app/frontend/src/javascript/controllers/events.js.erb @@ -178,11 +178,11 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' // Message displayed to the end user about rules that applies to events reservations $scope.eventExplicationsAlert = settingsPromise.event_explications_alert; - // the application global settings, required in - $scope.settings = settingsPromise - - // the moment when the slot selection changed for the last time, used to trigger changes in the cart - $scope.selectionTime = null; + // online payments (by card) + $scope.onlinePayment = { + showModal: false, + cartItems: undefined + }; /** * Callback to delete the provided event (admins only) @@ -313,7 +313,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' if (settingsPromise.online_payment_module !== 'true') { growl.error(_t('app.public.events_show.online_payment_disabled')); } else { - return payByStripe(reservation); + return payOnline(reservation); } } else { if (AuthService.isAuthorized('admin') @@ -381,7 +381,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' /** * Callback to cancel a reservation - * @param reservation {{id:number, reservable_id:number, nb_reserve_places:number}} + * @param reservation {{id:number, reservable_id:number, nb_reserve_places:number, slots_attributes:[{id: number, canceled_at: string}], total_booked_seats: number}} */ $scope.cancelReservation = function(reservation) { dialogs.confirm({ @@ -395,13 +395,13 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' } }, function() { // cancel confirmed Slot.cancel({ - id: reservation.slots[0].id + id: reservation.slots_attributes[0].id }, function() { // successfully canceled let index; growl.success(_t('app.public.events_show.reservation_was_successfully_cancelled')); index = $scope.reservations.indexOf(reservation); $scope.event.nb_free_places = $scope.event.nb_free_places + reservation.total_booked_seats; - $scope.reservations[index].slots[0].canceled_at = new Date(); + $scope.reservations[index].slots_attributes[0].canceled_at = new Date(); }, function(error) { growl.warning(_t('app.public.events_show.cancellation_failed')); }); @@ -410,11 +410,11 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' /** * Test if the provided reservation has been cancelled - * @param reservation {Reservation} + * @param reservation {{slots_attributes: [{canceled_at: string}]}} * @returns {boolean} */ $scope.isCancelled = function(reservation) { - return !!(reservation.slots[0].canceled_at); + return !!(reservation.slots_attributes[0].canceled_at); } /** @@ -451,10 +451,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' return eventToPlace = e; } }); - $scope.reservation.slots[0].start_at = eventToPlace.start_date; - $scope.reservation.slots[0].end_at = eventToPlace.end_date; - $scope.reservation.slots[0].availability_id = eventToPlace.availability_id; - $scope.reservation.slots_attributes = $scope.reservation.slots; + $scope.reservation.slots_attributes[0].start_at = eventToPlace.start_date; + $scope.reservation.slots_attributes[0].end_at = eventToPlace.end_date; + $scope.reservation.slots_attributes[0].availability_id = eventToPlace.availability_id; $scope.attempting = true; Reservation.update({ id: reservation.id }, { reservation: $scope.reservation }, function (reservation) { $uibModalInstance.close(reservation); @@ -491,10 +490,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' /** * Checks if the provided reservation is able to be moved (date change) - * @param reservation {{slots:[], total_booked_seats:number}} + * @param reservation {{slots_attributes:[], total_booked_seats:number}} */ $scope.reservationCanModify = function (reservation) { - const slotStart = moment(reservation.slots[0].start_at); + const slotStart = moment(reservation.slots_attributes[0].start_at); const now = moment(); let isAble = false; @@ -506,12 +505,11 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' /** * Checks if the provided reservation is able to be cancelled - * @param reservation {{slots:[]}} + * @param reservation {{slots_attributes:[]}} */ $scope.reservationCanCancel = function(reservation) { - var now, slotStart; - slotStart = moment(reservation.slots[0].start_at); - now = moment(); + const slotStart = moment(reservation.slots_attributes[0].start_at); + const now = moment(); return $scope.enableBookingCancel && slotStart.diff(now, "hours") >= $scope.cancelBookingDelay; }; @@ -554,6 +552,28 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' } }; + /** + * This will open/close the online payment modal + */ + $scope.toggleOnlinePaymentModal = (beforeApply) => { + setTimeout(() => { + $scope.onlinePayment.showModal = !$scope.onlinePayment.showModal; + if (typeof beforeApply === 'function') { + beforeApply(); + } + $scope.$apply(); + }, 50); + }; + + /** + * Invoked atfer a successful card payment + * @param reservation {*} reservation + */ + $scope.afterOnlinePaymentSuccess = (reservation) => { + $scope.toggleOnlinePaymentModal(); + afterPayment(reservation); + }; + /* PRIVATE SCOPE */ /** @@ -570,7 +590,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' // initialize the "reserve" object with the event's data resetEventReserve(); - // if non-admin, get the current user's reservations into $scope.reservations + // get the current user's reservations into $scope.reservations if ($scope.currentUser) { getReservations($scope.event.id, 'Event', $scope.currentUser.id); } @@ -677,71 +697,15 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' * Open a modal window which trigger the stripe payment process * @param reservation {Object} to book */ - const payByStripe = function (reservation) { - $uibModal.open({ - templateUrl: '/stripe/payment_modal.html', - size: 'md', - resolve: { - reservation () { - return reservation; - }, - price () { - return Price.compute(mkCartItems(reservation, $scope.coupon.applied, 'card')).$promise; - }, - wallet () { - return Wallet.getWalletByUser({ user_id: $scope.ctrl.member.id }).$promise; - }, - cgv () { - return CustomAsset.get({ name: 'cgv-file' }).$promise; - }, - objectToPay () { - return { - eventToReserve: $scope.event, - reserve: $scope.reserve, - member: $scope.ctrl.member - }; - }, - coupon () { - return $scope.coupon.applied; - }, - cartItems () { - return mkCartItems(reservation, $scope.coupon.applied, 'card'); - }, - stripeKey: ['Setting', function (Setting) { return Setting.get({ name: 'stripe_public_key' }).$promise; }] - }, - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$filter', 'coupon', 'cartItems', 'stripeKey', - function ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $filter, coupon, cartItems, stripeKey) { - // User's wallet amount - $scope.wallet = wallet; - - // Price - $scope.price = price.price; - - // Amount to pay - $scope.amount = helpers.getAmountToPay(price.price, wallet.amount); - - // Cart items - $scope.cartItems = cartItems; - - // CGV - $scope.cgv = cgv.custom_asset; - - // Reservation - $scope.reservation = reservation; - - // Used in wallet info template to interpolate some translations - $scope.numberFilter = $filter('number'); - - // stripe publishable key - $scope.stripeKey = stripeKey.setting.value; - - // Callback to handle the post-payment and reservation - $scope.onPaymentSuccess = function (reservation) { - $uibModalInstance.close(reservation); - }; - } - ] - }).result['finally'](null).then(function (reservation) { afterPayment(reservation); }); + const payOnline = function (reservation) { + // check that the online payment is enabled + if (settingsPromise.online_payment_module !== 'true') { + growl.error(_t('app.shared.cart.online_payment_disabled')); + } else { + $scope.toggleOnlinePaymentModal(() => { + $scope.onlinePayment.cartItems = mkCartItems(reservation, 'card'); + }); + } }; /** @@ -749,7 +713,6 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' * @param reservation {Object} to book */ const payOnSite = function (reservation) { - // FIXME, this may be broken, see cart.js#payOnSite $uibModal.open({ templateUrl: '/shared/valid_reservation_modal.html', size: 'sm', diff --git a/app/frontend/templates/events/show.html b/app/frontend/templates/events/show.html index eb4a6a2e1..a8a334d0f 100644 --- a/app/frontend/templates/events/show.html +++ b/app/frontend/templates/events/show.html @@ -66,18 +66,6 @@ - - -

{{ 'app.public.events_show.information_and_booking' }}

@@ -215,5 +203,13 @@
+
+ +
diff --git a/app/frontend/templates/stripe/payment_modal.html b/app/frontend/templates/stripe/payment_modal.html deleted file mode 100644 index d35dfaba2..000000000 --- a/app/frontend/templates/stripe/payment_modal.html +++ /dev/null @@ -1,55 +0,0 @@ -
- - - - - - -
diff --git a/app/models/cart_item/event_reservation.rb b/app/models/cart_item/event_reservation.rb index 7890316a4..a7bf0475e 100644 --- a/app/models/cart_item/event_reservation.rb +++ b/app/models/cart_item/event_reservation.rb @@ -8,7 +8,7 @@ class CartItem::EventReservation < CartItem::Reservation raise TypeError unless event.is_a? Event super(customer, operator, event, slots) - @normal_tickets = normal_tickets + @normal_tickets = normal_tickets || 0 @other_tickets = other_tickets || [] end diff --git a/app/views/api/payment_schedules/_payment_schedule.json.jbuilder b/app/views/api/payment_schedules/_payment_schedule.json.jbuilder index 465300fad..1480cdf0f 100644 --- a/app/views/api/payment_schedules/_payment_schedule.json.jbuilder +++ b/app/views/api/payment_schedules/_payment_schedule.json.jbuilder @@ -16,5 +16,5 @@ end json.items payment_schedule.payment_schedule_items do |item| json.extract! item, :id, :due_date, :state, :invoice_id, :payment_method json.amount item.amount / 100.00 - json.client_secret item.payment_intent.client_secret if item.stp_invoice_id && item.state == 'requires_action' + json.client_secret item.payment_intent.client_secret if item.payment_gateway_object && item.state == 'requires_action' end diff --git a/app/views/api/reservations/_reservation.json.jbuilder b/app/views/api/reservations/_reservation.json.jbuilder index b3873a7e0..b2be48b4b 100644 --- a/app/views/api/reservations/_reservation.json.jbuilder +++ b/app/views/api/reservations/_reservation.json.jbuilder @@ -1,8 +1,10 @@ +# frozen_string_literal: true + json.id reservation.id json.user_id reservation.statistic_profile.user_id json.user_full_name reservation.user.profile.full_name json.message reservation.message -json.slots reservation.slots do |s| +json.slots_attributes reservation.slots do |s| json.id s.id json.start_at s.start_at.iso8601 json.end_at s.end_at.iso8601 diff --git a/app/views/api/reservations/show.json.jbuilder b/app/views/api/reservations/show.json.jbuilder index 2e86ca070..9879f5bd0 100644 --- a/app/views/api/reservations/show.json.jbuilder +++ b/app/views/api/reservations/show.json.jbuilder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + json.id @reservation.id json.user_id @reservation.statistic_profile.user_id json.user do @@ -16,7 +18,7 @@ json.user do end end json.message @reservation.message -json.slots @reservation.slots do |s| +json.slots_attributes @reservation.slots do |s| json.id s.id json.start_at s.start_at.iso8601 json.end_at s.end_at.iso8601