diff --git a/.fabmanager-version b/.fabmanager-version index 79a614418f..26f8b8bcdf 100644 --- a/.fabmanager-version +++ b/.fabmanager-version @@ -1 +1 @@ -2.4.4 +2.4.5 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a54faf3d9d..7c036fc9d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog Fab Manager +## v2.4.5 2016 November 29 + +- Ability to create coupons with cash amounts (previously only percentages were allowed) +- Improved error messages when something wrong append when paying a machine reservation by stripe +- Ability to display optional information message on event reservation page +- Fix a bug: misconfigured Twitter's ENV variables results in HTTP error 500 +- Fix a bug: wallet is not debited when paying locally with a user who have invoices disabled +- Fix a bug: wrong error message about rounding inconsistency is logged on invoice generation +- Fix a bug: reservation calendar of a specific training shows availabilities for all trainings + ## v2.4.4 2016 November 24 - Fix a bug: unable to rollback migration 20160906145713 diff --git a/README.md b/README.md index fb6d7023ca..2a08d75726 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,7 @@ See https://help.disqus.com/customer/portal/articles/466208-what-s-a-shortname- TWITTER_NAME Identifier of the Twitter account, from witch the last tweet will be fetched and displayed on the home page. +This value can be graphically overridden during the application's lifecycle in Admin/Customization/Home page/Twitter Feed. It will also be used for [Twitter Card analytics](https://dev.twitter.com/cards/analytics). TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, TWITTER_ACCESS_TOKEN & TWITTER_ACCESS_TOKEN_SECRET diff --git a/app/assets/javascripts/controllers/admin/coupons.coffee b/app/assets/javascripts/controllers/admin/coupons.coffee index eef8f658c4..21e0a65ae7 100644 --- a/app/assets/javascripts/controllers/admin/coupons.coffee +++ b/app/assets/javascripts/controllers/admin/coupons.coffee @@ -8,12 +8,13 @@ userValidities = ['once', 'forever'] ## # Controller used in the coupon creation page ## -Application.Controllers.controller "NewCouponController", ["$scope", "$state",'Coupon', 'growl', '_t' +Application.Controllers.controller "NewCouponController", ["$scope", "$state", 'Coupon', 'growl', '_t' , ($scope, $state, Coupon, growl, _t) -> ## Values for the coupon currently created $scope.coupon = active: true + type: 'percent_off' ## Options for the validity per user $scope.validities = userValidities @@ -57,7 +58,7 @@ Application.Controllers.controller "NewCouponController", ["$scope", "$state",'C ## # Controller used in the coupon edition page ## -Application.Controllers.controller "EditCouponController", ["$scope", "$state", 'Coupon', 'couponPromise', '_t' +Application.Controllers.controller "EditCouponController", ["$scope", "$state", 'Coupon', 'couponPromise', '_t' , ($scope, $state, Coupon, couponPromise, _t) -> ### PUBLIC SCOPE ### diff --git a/app/assets/javascripts/controllers/admin/events.coffee.erb b/app/assets/javascripts/controllers/admin/events.coffee.erb index 4e2665f5da..9ef91e1c5d 100644 --- a/app/assets/javascripts/controllers/admin/events.coffee.erb +++ b/app/assets/javascripts/controllers/admin/events.coffee.erb @@ -382,8 +382,8 @@ Application.Controllers.controller "ShowEventReservationsController", ["$scope", ## # Controller used in the event creation page ## -Application.Controllers.controller "NewEventController", ["$scope", "$state", "$locale", 'CSRF', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise', '_t' -, ($scope, $state, $locale, CSRF, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise, _t) -> +Application.Controllers.controller "NewEventController", ["$scope", "$state", 'CSRF', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise', '_t' +, ($scope, $state, CSRF, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise, _t) -> CSRF.setMetaTags() ## API URL where the form will be posted @@ -425,10 +425,6 @@ Application.Controllers.controller "NewEventController", ["$scope", "$state", "$ {label: _t('every_year'), value: 'year'} ] - ## currency symbol for the current locale (cf. angular-i18n) - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM; - - ## Using the EventsController new EventsController($scope, $state) ] @@ -438,8 +434,8 @@ Application.Controllers.controller "NewEventController", ["$scope", "$state", "$ ## # Controller used in the events edition page ## -Application.Controllers.controller "EditEventController", ["$scope", "$state", "$stateParams", "$locale", 'CSRF', 'eventPromise', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise' -, ($scope, $state, $stateParams, $locale, CSRF, eventPromise, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise) -> +Application.Controllers.controller "EditEventController", ["$scope", "$state", "$stateParams", 'CSRF', 'eventPromise', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise' +, ($scope, $state, $stateParams, CSRF, eventPromise, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise) -> ### PUBLIC SCOPE ### @@ -454,9 +450,6 @@ Application.Controllers.controller "EditEventController", ["$scope", "$state", " ## Retrieve the event details, in case of error the user is redirected to the events listing $scope.event = eventPromise - ## currency symbol for the current locale (cf. angular-i18n) - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM; - ## List of categories for the events $scope.categories = categoriesPromise diff --git a/app/assets/javascripts/controllers/admin/members.coffee.erb b/app/assets/javascripts/controllers/admin/members.coffee.erb index 75b30126cb..9c37206d8e 100644 --- a/app/assets/javascripts/controllers/admin/members.coffee.erb +++ b/app/assets/javascripts/controllers/admin/members.coffee.erb @@ -421,10 +421,7 @@ Application.Controllers.controller "EditMemberController", ["$scope", "$state", modalInstance = $uibModal.open animation: true, templateUrl: '<%= asset_path "wallet/credit_modal.html" %>' - controller: ['$scope', '$uibModalInstance', 'Wallet', '$locale', ($scope, $uibModalInstance, Wallet, $locale) -> - - ## currency symbol for the current locale (cf. angular-i18n) - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + controller: ['$scope', '$uibModalInstance', 'Wallet', ($scope, $uibModalInstance, Wallet) -> ## # Modal dialog validation callback diff --git a/app/assets/javascripts/controllers/admin/plans.coffee.erb b/app/assets/javascripts/controllers/admin/plans.coffee.erb index 8fb323c10f..c632790b8d 100644 --- a/app/assets/javascripts/controllers/admin/plans.coffee.erb +++ b/app/assets/javascripts/controllers/admin/plans.coffee.erb @@ -78,8 +78,8 @@ class PlanController ## # Controller used in the plan creation form ## -Application.Controllers.controller 'NewPlanController', ['$scope', '$uibModal', 'groups', 'plans', 'machines', 'prices', 'partners', 'CSRF', '$state', 'growl', '_t', '$locale' -, ($scope, $uibModal, groups, plans, machines, prices, partners, CSRF, $state, growl, _t, $locale) -> +Application.Controllers.controller 'NewPlanController', ['$scope', '$uibModal', 'groups', 'plans', 'machines', 'prices', 'partners', 'CSRF', '$state', 'growl', '_t' +, ($scope, $uibModal, groups, plans, machines, prices, partners, CSRF, $state, growl, _t) -> @@ -119,10 +119,6 @@ Application.Controllers.controller 'NewPlanController', ['$scope', '$uibModal', $scope.method = 'POST' - ## currency symbol for the current locale (cf. angular-i18n) - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM; - - ## # Checks if the partner contact is a valid data. Used in the form validation process @@ -187,8 +183,8 @@ Application.Controllers.controller 'NewPlanController', ['$scope', '$uibModal', ## # Controller used in the plan edition form ## -Application.Controllers.controller 'EditPlanController', ['$scope', 'groups', 'plans', 'planPromise', 'machines', 'prices', 'partners', 'CSRF', '$state', '$stateParams', 'growl', '$filter', '_t', '$locale', 'Plan' -, ($scope, groups, plans, planPromise, machines, prices, partners, CSRF, $state, $stateParams, growl, $filter, _t, $locale, Plan) -> +Application.Controllers.controller 'EditPlanController', ['$scope', 'groups', 'plans', 'planPromise', 'machines', 'prices', 'partners', 'CSRF', '$state', '$stateParams', 'growl', '$filter', '_t', 'Plan' +, ($scope, groups, plans, planPromise, machines, prices, partners, CSRF, $state, $stateParams, growl, $filter, _t, Plan) -> @@ -208,10 +204,6 @@ Application.Controllers.controller 'EditPlanController', ['$scope', 'groups', 'p $scope.method = 'PATCH' - ## currency symbol for the current locale (cf. angular-i18n) - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM; - - ## # If a parent plan was set ($scope.plan.parent), the prices will be copied from this parent plan into diff --git a/app/assets/javascripts/controllers/admin/pricing.coffee.erb b/app/assets/javascripts/controllers/admin/pricing.coffee.erb index 4d69bfae72..76bcf7cf68 100644 --- a/app/assets/javascripts/controllers/admin/pricing.coffee.erb +++ b/app/assets/javascripts/controllers/admin/pricing.coffee.erb @@ -3,8 +3,8 @@ ## # Controller used in the prices edition page ## -Application.Controllers.controller "EditPricingController", ["$scope", "$state", '$uibModal', 'TrainingsPricing', '$filter', 'Credit', 'Pricing', 'Plan', 'Coupon', 'plans', 'groups', 'growl', 'machinesPricesPromise', 'Price', 'dialogs', 'trainingsPricingsPromise', 'trainingsPromise', 'machineCreditsPromise', 'machinesPromise', 'trainingCreditsPromise', 'couponsPromise', '_t' -, ($scope, $state, $uibModal, TrainingsPricing, $filter, Credit, Pricing, Plan, Coupon, plans, groups, growl, machinesPricesPromise, Price, dialogs, trainingsPricingsPromise, trainingsPromise, machineCreditsPromise, machinesPromise, trainingCreditsPromise, couponsPromise, _t) -> +Application.Controllers.controller "EditPricingController", ["$scope", "$state", '$uibModal', '$filter', 'TrainingsPricing', 'Credit', 'Pricing', 'Plan', 'Coupon', 'plans', 'groups', 'growl', 'machinesPricesPromise', 'Price', 'dialogs', 'trainingsPricingsPromise', 'trainingsPromise', 'machineCreditsPromise', 'machinesPromise', 'trainingCreditsPromise', 'couponsPromise', '_t' +, ($scope, $state, $uibModal, $filter, TrainingsPricing, Credit, Pricing, Plan, Coupon, plans, groups, growl, machinesPricesPromise, Price, dialogs, trainingsPricingsPromise, trainingsPromise, machineCreditsPromise, machinesPromise, trainingCreditsPromise, couponsPromise, _t) -> ### PUBLIC SCOPE ### ## List of machines prices (not considering any plan) diff --git a/app/assets/javascripts/controllers/admin/settings.coffee b/app/assets/javascripts/controllers/admin/settings.coffee index ea83a15251..a1e3b94a84 100644 --- a/app/assets/javascripts/controllers/admin/settings.coffee +++ b/app/assets/javascripts/controllers/admin/settings.coffee @@ -45,6 +45,7 @@ Application.Controllers.controller "SettingsController", ["$scope", 'Setting', ' $scope.trainingExplicationsAlert = { name: 'training_explications_alert', value: settingsPromise.training_explications_alert } $scope.trainingInformationMessage = { name: 'training_information_message', value: settingsPromise.training_information_message} $scope.subscriptionExplicationsAlert = { name: 'subscription_explications_alert', value: settingsPromise.subscription_explications_alert } + $scope.eventExplicationsAlert = {name: 'event_explications_alert', value: settingsPromise.event_explications_alert } $scope.windowStart = { name: 'booking_window_start', value: settingsPromise.booking_window_start } $scope.windowEnd = { name: 'booking_window_end', value: settingsPromise.booking_window_end } $scope.mainColorSetting = { name: 'main_color', value: settingsPromise.main_color } @@ -73,7 +74,7 @@ Application.Controllers.controller "SettingsController", ["$scope", 'Setting', ' $scope.cancelDelay = name: 'booking_cancel_delay' value: parseInt(settingsPromise.booking_cancel_delay) - + $scope.enableReminder = name: 'reminder_enable' value: (settingsPromise.reminder_enable == 'true') diff --git a/app/assets/javascripts/controllers/application.coffee.erb b/app/assets/javascripts/controllers/application.coffee.erb index 095c50892a..51c42a6a36 100644 --- a/app/assets/javascripts/controllers/application.coffee.erb +++ b/app/assets/javascripts/controllers/application.coffee.erb @@ -1,7 +1,7 @@ 'use strict' -Application.Controllers.controller 'ApplicationController', ["$rootScope", "$scope", "$window", "Session", "AuthService", "Auth", "$uibModal", "$state", 'growl', 'Notification', '$interval', "Setting", '_t', 'Version' -, ($rootScope, $scope, $window, Session, AuthService, Auth, $uibModal, $state, growl, Notification, $interval, Setting, _t, Version) -> +Application.Controllers.controller 'ApplicationController', ["$rootScope", "$scope", "$window", '$locale', "Session", "AuthService", "Auth", "$uibModal", "$state", 'growl', 'Notification', '$interval', "Setting", '_t', 'Version' +, ($rootScope, $scope, $window, $locale, Session, AuthService, Auth, $uibModal, $state, growl, Notification, $interval, Setting, _t, Version) -> @@ -18,6 +18,10 @@ Application.Controllers.controller 'ApplicationController', ["$rootScope", "$sco $scope.version = version: '' + ## currency symbol for the current locale (cf. angular-i18n) + $rootScope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM; + + ## # Set the current user to the provided value and initialize the session # @param user {Object} Rails/Devise user diff --git a/app/assets/javascripts/controllers/events.coffee.erb b/app/assets/javascripts/controllers/events.coffee.erb index 39083272f5..58ec0fe312 100644 --- a/app/assets/javascripts/controllers/events.coffee.erb +++ b/app/assets/javascripts/controllers/events.coffee.erb @@ -154,6 +154,7 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " tickets: {} toReserve: false amountTotal : 0 + totalNoCoupon: 0 totalSeats: 0 ## Discount coupon to apply to the basket, if any @@ -172,6 +173,9 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " ## Global config: delay in hours before a booking while changing the booking slot is forbidden $scope.moveBookingDelay = parseInt(settingsPromise.booking_move_delay) + ## Message displayed to the end user about rules that applies to events reservations + $scope.eventExplicationsAlert = settingsPromise.event_explications_alert + ## @@ -400,6 +404,7 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " r = mkReservation($scope.ctrl.member, $scope.reserve, $scope.event) Price.compute mkRequestParams(r, $scope.coupon.applied), (res) -> $scope.reserve.amountTotal = res.price + $scope.reserve.totalNoCoupon = res.price_without_coupon else $scope.reserve.amountTotal = null @@ -560,9 +565,9 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " member: $scope.ctrl.member coupon: -> $scope.coupon.applied - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$locale', '$filter', 'coupon', - ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $locale, $filter, coupon) -> - # user wallet amount + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$filter', 'coupon', + ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $filter, coupon) -> + # User's wallet amount $scope.walletAmount = wallet.amount # Price @@ -574,8 +579,7 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " # Reservation $scope.reservation = reservation - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM - + # Used in wallet info template to interpolate some translations $scope.numberFilter = $filter('number') # Callback for the stripe payment authorization @@ -616,9 +620,9 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " Wallet.getWalletByUser({user_id: reservation.user_id}).$promise coupon: -> $scope.coupon.applied - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', '$locale', 'helpers', '$filter', 'coupon', - ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, $locale, helpers, $filter, coupon) -> - # user wallet amount + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', 'coupon', + ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, helpers, $filter, coupon) -> + # User's wallet amount $scope.walletAmount = wallet.amount # Price @@ -630,8 +634,7 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " # Reservation $scope.reservation = reservation - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM - + # Used in wallet info template to interpolate some translations $scope.numberFilter = $filter('number') # Button label @@ -675,6 +678,7 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " $scope.event.nb_free_places = $scope.event.nb_free_places - reservation.total_booked_seats resetEventReserve() $scope.reserveSuccess = true + $scope.coupon.applied = null $scope.reservations.push reservation if $scope.currentUser.role == 'admin' $scope.ctrl.member = null diff --git a/app/assets/javascripts/controllers/machines.coffee.erb b/app/assets/javascripts/controllers/machines.coffee.erb index 775dd441ab..6234165e52 100644 --- a/app/assets/javascripts/controllers/machines.coffee.erb +++ b/app/assets/javascripts/controllers/machines.coffee.erb @@ -315,6 +315,9 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat ## total amount of the bill to pay $scope.amountTotal = 0 + ## total amount of the elements in the cart, without considering any coupon + $scope.totalNoCoupon = 0 + ## Discount coupon to apply to the basket, if any $scope.coupon = applied: null @@ -661,6 +664,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat r = mkReservation($scope.ctrl.member, $scope.eventsReserved, $scope.selectedPlan) Price.compute mkRequestParams(r, $scope.coupon.applied), (res) -> $scope.amountTotal = res.price + $scope.totalNoCoupon = res.price_without_coupon setSlotsDetails(res.details) else # otherwise we alert, this error musn't occur when the current user is not admin @@ -782,8 +786,8 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat CustomAsset.get({name: 'cgv-file'}).$promise coupon: -> $scope.coupon.applied - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'wallet', 'helpers', '$locale', '$filter', 'coupon', - ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, wallet, helpers, $locale, $filter, coupon) -> + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', 'coupon', + ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, wallet, helpers, $filter, coupon) -> # user wallet amount $scope.walletAmount = wallet.amount @@ -796,9 +800,6 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat # Reservation $scope.reservation = reservation - # Currency symbol or abreviation for the current locale - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM - # Used in wallet info template to interpolate some translations $scope.numberFilter = $filter('number') @@ -815,9 +816,19 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat $uibModalInstance.close(reservation) , (response)-> $scope.alerts = [] - $scope.alerts.push - msg: response.data.card[0] - type: 'danger' + if response.status == 500 + $scope.alerts.push + msg: response.statusText + type: 'danger' + else + if response.data.card and response.data.card.join('').length > 0 + $scope.alerts.push + msg: response.data.card.join('. ') + type: 'danger' + else if response.data.payment and response.data.payment.join('').length > 0 + $scope.alerts.push + msg: response.data.payment.join('. ') + type: 'danger' $scope.attempting = false ] .result['finally'](null).then (reservation)-> @@ -842,8 +853,8 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat Wallet.getWalletByUser({user_id: reservation.user_id}).$promise coupon: -> $scope.coupon.applied - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', '$locale', 'coupon', - ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, helpers, $filter, $locale, coupon) -> + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', 'coupon', + ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, helpers, $filter, coupon) -> # user wallet amount $scope.walletAmount = wallet.amount @@ -857,9 +868,6 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat # Reservation $scope.reservation = reservation - # Currency symbol or abreviation for the current locale - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM - # Used in wallet info template to interpolate some translations $scope.numberFilter = $filter('number') @@ -942,6 +950,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat $scope.paidMachineSlots = $scope.eventsReserved $scope.eventsReserved = [] + $scope.coupon.applied = null if $scope.selectedPlan $scope.ctrl.member.subscribed_plan = angular.copy($scope.selectedPlan) diff --git a/app/assets/javascripts/controllers/plans.coffee.erb b/app/assets/javascripts/controllers/plans.coffee.erb index 14c74443e6..4396d03e9c 100644 --- a/app/assets/javascripts/controllers/plans.coffee.erb +++ b/app/assets/javascripts/controllers/plans.coffee.erb @@ -177,11 +177,14 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop updateCartPrice = -> # first we check that a user was selected if Object.keys($scope.ctrl.member).length > 0 - $scope.cart.total = $scope.selectedPlan.amount - # apply the coupon if any - if $scope.coupon.applied + $scope.cart.total = $scope.selectedPlan.amount + # apply the coupon if any + if $scope.coupon.applied + if $scope.coupon.applied.type == 'percent_off' discount = $scope.cart.total * $scope.coupon.applied.percent_off / 100 - $scope.cart.total -= discount + else if $scope.coupon.applied.type == 'amount_off' + discount = $scope.coupon.applied.amount_off + $scope.cart.total -= discount else $scope.reserve.amountTotal = null @@ -200,9 +203,9 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop wallet: -> Wallet.getWalletByUser({user_id: $scope.ctrl.member.id}).$promise coupon: -> $scope.coupon.applied - controller: ['$scope', '$uibModalInstance', '$state', 'selectedPlan', 'member', 'price', 'Subscription', 'CustomAsset', 'wallet', 'helpers', '$locale', '$filter', 'coupon', - ($scope, $uibModalInstance, $state, selectedPlan, member, price, Subscription, CustomAsset, wallet, helpers, $locale, $filter, coupon) -> - # user wallet amount + controller: ['$scope', '$uibModalInstance', '$state', 'selectedPlan', 'member', 'price', 'Subscription', 'CustomAsset', 'wallet', 'helpers', '$filter', 'coupon', + ($scope, $uibModalInstance, $state, selectedPlan, member, price, Subscription, CustomAsset, wallet, helpers, $filter, coupon) -> + # User's wallet amount $scope.walletAmount = wallet.amount # Final price to pay by the user @@ -211,9 +214,6 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop # The plan that the user is about to subscribe $scope.selectedPlan = selectedPlan - # Currency symbol or abreviation for the current locale - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM - # Used in wallet info template to interpolate some translations $scope.numberFilter = $filter('number') @@ -249,6 +249,7 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop Auth._currentUser.subscribed_plan = angular.copy($scope.selectedPlan) $scope.paid.plan = angular.copy($scope.selectedPlan) $scope.selectedPlan = null + $scope.coupon.applied = null @@ -266,8 +267,8 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop wallet: -> Wallet.getWalletByUser({user_id: $scope.ctrl.member.id}).$promise coupon: -> $scope.coupon.applied - controller: ['$scope', '$uibModalInstance', '$state', 'selectedPlan', 'member', 'price', 'Subscription', 'wallet', 'helpers', '$locale', '$filter', 'coupon', - ($scope, $uibModalInstance, $state, selectedPlan, member, price, Subscription, wallet, helpers, $locale, $filter, coupon) -> + controller: ['$scope', '$uibModalInstance', '$state', 'selectedPlan', 'member', 'price', 'Subscription', 'wallet', 'helpers', '$filter', 'coupon', + ($scope, $uibModalInstance, $state, selectedPlan, member, price, Subscription, wallet, helpers, $filter, coupon) -> # user wallet amount $scope.walletAmount = wallet.amount @@ -277,9 +278,6 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop # price to pay $scope.amount = helpers.getAmountToPay($scope.price, wallet.amount) - # Currency symbol or abreviation for the current locale - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM - # Used in wallet info template to interpolate some translations $scope.numberFilter = $filter('number') @@ -329,6 +327,7 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop $scope.ctrl.member = null $scope.paid.plan = angular.copy($scope.selectedPlan) $scope.selectedPlan = null + $scope.coupon.applied = null diff --git a/app/assets/javascripts/controllers/projects.coffee.erb b/app/assets/javascripts/controllers/projects.coffee.erb index 5e7979e742..0252de56fc 100644 --- a/app/assets/javascripts/controllers/projects.coffee.erb +++ b/app/assets/javascripts/controllers/projects.coffee.erb @@ -217,10 +217,25 @@ Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'P # Number of projects added to the page when the user clicks on 'load more projects' PROJECTS_PER_PAGE = 16 - $scope.openlabAppId = Fablab.openlabAppId + ### PUBLIC SCOPE ### - $scope.search = { q: ($location.$$search.q || ""), from: ($location.$$search.from || undefined), machine_id: (parseInt($location.$$search.machine_id) || undefined), component_id: (parseInt($location.$$search.component_id) || undefined), theme_id: (parseInt($location.$$search.theme_id) || undefined) } + + ## Fab-manager's instance ID in the openLab network + $scope.openlabAppId = Fablab.openlabAppId + + ## Is openLab enabled on the instance? + $scope.openlab = + projectsActive: Fablab.openlabProjectsActive + searchOverWholeNetwork: false + + ## default search parameters + $scope.search = + q: ($location.$$search.q || "") + from: ($location.$$search.from || undefined) + machine_id: (parseInt($location.$$search.machine_id) || undefined) + component_id: (parseInt($location.$$search.component_id) || undefined) + theme_id: (parseInt($location.$$search.theme_id) || undefined) ## list of projects to display $scope.projects = [] @@ -234,32 +249,14 @@ Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'P ## list of components / used for filtering $scope.components = componentsPromise - $scope.openlab = {} - $scope.openlab.projectsActive = Fablab.openlabProjectsActive - - if $location.$$search.whole_network is 'f' - $scope.openlab.searchOverWholeNetwork = false - else - $scope.openlab.searchOverWholeNetwork = $scope.openlab.projectsActive || false - normalizeProjectsAttrs = (projects)-> - projects.map((project)-> - project.project_image = project.image_url - return project - ) $scope.searchOverWholeNetworkChanged = -> setTimeout -> $scope.resetFiltersAndTriggerSearch() , 150 - loadMoreCallback = (projectsPromise)-> - $scope.projects = $scope.projects.concat(projectsPromise.projects) - updateUrlParam('page', $scope.projectsPagination.currentPage) - loadMoreOpenlabCallback = (projectsPromise)-> - $scope.projects = $scope.projects.concat(normalizeProjectsAttrs(projectsPromise.projects)) - updateUrlParam('page', $scope.projectsPagination.currentPage) $scope.loadMore = -> if $scope.openlab.searchOverWholeNetwork is true @@ -268,6 +265,7 @@ Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'P $scope.projectsPagination.loadMore(search: $scope.search) + $scope.resetFiltersAndTriggerSearch = -> $scope.search.q = "" $scope.search.from = undefined @@ -277,6 +275,8 @@ Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'P $scope.setUrlQueryParams($scope.search) $scope.triggerSearch() + + $scope.triggerSearch = -> currentPage = parseInt($location.$$search.page) || 1 if $scope.openlab.searchOverWholeNetwork is true @@ -298,6 +298,8 @@ Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'P $scope.projectsPagination.totalCount = projectsPromise.meta.total $scope.projects = projectsPromise.projects + + ## # Callback to switch the user's view to the detailled project page # @param project {{slug:string}} The project to display @@ -309,6 +311,8 @@ Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'P else $state.go('app.public.projects_show', {id: project.slug}) + + ## # function to set all url query search parameters from search object ## @@ -320,6 +324,21 @@ Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'P updateUrlParam('component_id', search.component_id) updateUrlParam('machine_id', search.machine_id) + + + ### PRIVATE SCOPE ### + + ## + # Kind of constructor: these actions will be realized first when the controller is loaded + ## + initialize = -> + if $location.$$search.whole_network is 'f' + $scope.openlab.searchOverWholeNetwork = false + else + $scope.openlab.searchOverWholeNetwork = $scope.openlab.projectsActive || false + $scope.triggerSearch() + + ## # function to update url query param, little hack to turn off reloadOnSearch and re-enable it after setting the params # params example: 'q' , 'presse-purée' @@ -330,9 +349,30 @@ Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'P $timeout -> $state.current.reloadOnSearch = undefined - ## initialization - $scope.triggerSearch() + + loadMoreCallback = (projectsPromise)-> + $scope.projects = $scope.projects.concat(projectsPromise.projects) + updateUrlParam('page', $scope.projectsPagination.currentPage) + + + + loadMoreOpenlabCallback = (projectsPromise)-> + $scope.projects = $scope.projects.concat(normalizeProjectsAttrs(projectsPromise.projects)) + updateUrlParam('page', $scope.projectsPagination.currentPage) + + + + normalizeProjectsAttrs = (projects)-> + projects.map((project)-> + project.project_image = project.image_url + return project + ) + + + + ## !!! MUST BE CALLED AT THE END of the controller + initialize() ] diff --git a/app/assets/javascripts/controllers/trainings.coffee.erb b/app/assets/javascripts/controllers/trainings.coffee.erb index 2c6cdfa391..cdd4563b42 100644 --- a/app/assets/javascripts/controllers/trainings.coffee.erb +++ b/app/assets/javascripts/controllers/trainings.coffee.erb @@ -137,6 +137,12 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta $scope.coupon = applied: null + ## Total price of the cart, that the user will pay + $scope.amountTotal = 0 + + ## Total amount of the elements in the cart, without considering any coupon + $scope.totalNoCoupon = 0 + ## fullCalendar (v2) configuration $scope.calendarConfig = CalendarConfig minTime: moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss')) @@ -353,6 +359,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta r = mkReservation($scope.ctrl.member, $scope.selectedTraining, $scope.selectedPlan) Price.compute mkRequestParams(r, $scope.coupon.applied), (res) -> $scope.amountTotal = res.price + $scope.totalNoCoupon = res.price_without_coupon else $scope.amountTotal = null @@ -426,7 +433,6 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta if $scope.ctrl.member # reserve a training if this training will not be reserved and is not about to move and not is completed if !event.is_reserved && !$scope.slotToModify && !event.is_completed - $scope.coupon.applied = null if event != $scope.selectedTraining $scope.selectedTraining = event $scope.selectedTraining.offered = false @@ -533,9 +539,9 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta CustomAsset.get({name: 'cgv-file'}).$promise coupon: -> $scope.coupon.applied - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'wallet', 'cgv', 'Auth', 'Reservation', '$locale', 'helpers', '$filter', 'coupon' - ($scope, $uibModalInstance, $state, reservation, price, wallet, cgv, Auth, Reservation, $locale, helpers, $filter, coupon) -> - # user wallet amount + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'wallet', 'cgv', 'Auth', 'Reservation', 'helpers', '$filter', 'coupon' + ($scope, $uibModalInstance, $state, reservation, price, wallet, cgv, Auth, Reservation, helpers, $filter, coupon) -> + # User's wallet amount $scope.walletAmount = wallet.amount # Price @@ -547,8 +553,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta # Reservation $scope.reservation = reservation - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM - + # Used in wallet info template to interpolate some translations $scope.numberFilter = $filter('number') ## @@ -594,9 +599,9 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta Wallet.getWalletByUser({user_id: reservation.user_id}).$promise coupon: -> $scope.coupon.applied - controller: ['$scope', '$uibModalInstance', '$state', '$filter', 'reservation', 'price', 'wallet', 'Auth', 'Reservation', '$locale', 'helpers', 'coupon' - ($scope, $uibModalInstance, $state, $filter, reservation, price, wallet, Auth, Reservation, $locale, helpers, coupon) -> - # user wallet amount + controller: ['$scope', '$uibModalInstance', '$state', '$filter', 'reservation', 'price', 'wallet', 'Auth', 'Reservation', 'helpers', 'coupon' + ($scope, $uibModalInstance, $state, $filter, reservation, price, wallet, Auth, Reservation, helpers, coupon) -> + # User's wallet amount $scope.walletAmount = wallet.amount # Price @@ -608,8 +613,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta # Reservation $scope.reservation = reservation - $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM - + # Used in wallet info template to interpolate some translations $scope.numberFilter = $filter('number') # Button label @@ -649,7 +653,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta # first we check that a user was selected if Object.keys($scope.ctrl.member).length > 0 r = mkReservation($scope.ctrl.member, training) # reservation without any Plan -> we get the training price - Price.compute mkRequestParams(r, $scope.coupon.applied), (res) -> + Price.compute mkRequestParams(r), (res) -> $scope.selectedTrainingAmount = res.price else $scope.selectedTrainingAmount = null @@ -672,6 +676,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta $scope.selectedTraining = null $scope.trainingIsValid = false + $scope.coupon.applied = null if $scope.selectedPlan $scope.ctrl.member.subscribed_plan = angular.copy($scope.selectedPlan) diff --git a/app/assets/javascripts/directives/coupon.coffee.erb b/app/assets/javascripts/directives/coupon.coffee.erb index 2d1aa5812b..4392297bf7 100644 --- a/app/assets/javascripts/directives/coupon.coffee.erb +++ b/app/assets/javascripts/directives/coupon.coffee.erb @@ -1,11 +1,11 @@ -Application.Directives.directive 'coupon', [ 'Coupon', 'growl', '_t', (Coupon, growl, _t) -> +Application.Directives.directive 'coupon', [ '$rootScope', 'Coupon', 'growl', '_t', ($rootScope, Coupon, growl, _t) -> { restrict: 'E' scope: show: '=' coupon: '=' + total: '=' userId: '@' - hasSelectSlot: '=' templateUrl: '<%= asset_path "shared/_coupon.html" %>' link: ($scope, element, attributes) -> @@ -16,15 +16,13 @@ Application.Directives.directive 'coupon', [ 'Coupon', 'growl', '_t', (Coupon, g # Available status are: 'pending', 'valid', 'invalid' $scope.status = 'pending' - # Binding for the code inputed + # Binding for the code inputed (see the attached template) $scope.couponCode = null - $scope.$watch 'hasSelectSlot', (newValue) -> - unless newValue - $scope.coupon = null - $scope.couponCode = null - $scope.code.input = false - + # Re-compute if the code can be applied when the total of the cart changes + $scope.$watch 'total', (newValue, oldValue) -> + if newValue and newValue != oldValue and $scope.couponCode + $scope.validateCode() ## # Callback to validate the code @@ -34,10 +32,13 @@ Application.Directives.directive 'coupon', [ 'Coupon', 'growl', '_t', (Coupon, g $scope.status = 'pending' $scope.coupon = null else - Coupon.validate {code: $scope.couponCode, user_id: $scope.userId}, (res) -> + Coupon.validate {code: $scope.couponCode, user_id: $scope.userId, amount: $scope.total}, (res) -> $scope.status = 'valid' $scope.coupon = res - growl.success(_t('the_coupon_has_been_applied_you_get_PERCENT_discount', {PERCENT: res.percent_off})) + if res.type == 'percent_off' + growl.success(_t('the_coupon_has_been_applied_you_get_PERCENT_discount', {PERCENT: res.percent_off})) + else + growl.success(_t('the_coupon_has_been_applied_you_get_AMOUNT_CURRENCY', {AMOUNT: res.amount_off, CURRENCY: $rootScope.currencySymbol})) , (err) -> $scope.status = 'invalid' $scope.coupon = null diff --git a/app/assets/javascripts/router.coffee.erb b/app/assets/javascripts/router.coffee.erb index 5d7f16cc0e..b621517480 100644 --- a/app/assets/javascripts/router.coffee.erb +++ b/app/assets/javascripts/router.coffee.erb @@ -522,7 +522,7 @@ angular.module('application.router', ['ui.router']). PriceCategory.query().$promise ] settingsPromise: ['Setting', (Setting)-> - Setting.query(names: "['booking_move_enable', 'booking_move_delay']").$promise + Setting.query(names: "['booking_move_enable', 'booking_move_delay', 'event_explications_alert']").$promise ] translations: [ 'Translations', (Translations) -> Translations.query(['app.public.events_show', 'app.shared.member_select', 'app.shared.stripe', @@ -1037,6 +1037,7 @@ angular.module('application.router', ['ui.router']). 'training_explications_alert', 'training_information_message', 'subscription_explications_alert', + 'event_explications_alert', 'booking_window_start', 'booking_window_end', 'booking_move_enable', diff --git a/app/assets/stylesheets/app.utilities.scss b/app/assets/stylesheets/app.utilities.scss index eca31e6387..2457915158 100644 --- a/app/assets/stylesheets/app.utilities.scss +++ b/app/assets/stylesheets/app.utilities.scss @@ -56,6 +56,7 @@ p, .widget p { .block.hide{display: none;} .inline{display:inline-block !important;} .none{display: none;} +.pull-left{float: left;} .pull-right-lg{float: right;} .pull-none{float: none;} .rounded{border-radius: 500px;} diff --git a/app/assets/templates/admin/coupons/_form.html.erb b/app/assets/templates/admin/coupons/_form.html.erb index 81274bcc2c..4cc92c9f6e 100644 --- a/app/assets/templates/admin/coupons/_form.html.erb +++ b/app/assets/templates/admin/coupons/_form.html.erb @@ -21,7 +21,20 @@ {{ 'code_must_be_composed_of_capital_letters_digits_and_or_dashes' }} -
+
+ + +
+ +
+ ng-required="coupon.type == 'percent_off'"/>
{{ 'percent_off_is_required' }} {{ 'percentage_must_be_between_0_and_100' }}
+ +
+ +
+ {{currencySymbol}} + +
+ {{ 'percent_off_is_required' }} + {{ 'percentage_must_be_between_0_and_100' }} +
+