diff --git a/.rubocop.yml b/.rubocop.yml index fa5d4d57e..2eca3bfc2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -16,6 +16,7 @@ Metrics/BlockLength: - 'lib/tasks/**/*.rake' - 'config/routes.rb' - 'app/pdfs/pdf/*.rb' + - 'test/**/*.rb' Metrics/ParameterLists: CountKeywordArgs: false Style/BracesAroundHashParameters: diff --git a/Rakefile b/Rakefile index ba6b733dd..e85f91391 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,6 @@ # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. -require File.expand_path('../config/application', __FILE__) +require_relative 'config/application' Rails.application.load_tasks diff --git a/app/controllers/api/payments_controller.rb b/app/controllers/api/payments_controller.rb index 6732ebddb..ad1eaa2dd 100644 --- a/app/controllers/api/payments_controller.rb +++ b/app/controllers/api/payments_controller.rb @@ -49,7 +49,7 @@ class API::PaymentsController < API::ApiController if params[:cart_items][:reservation] res = on_reservation_success(intent, amount[:details]) elsif params[:cart_items][:subscription] - res = on_subscription_success(intent) + res = on_subscription_success(intent, amount[:details]) end end @@ -72,7 +72,7 @@ class API::PaymentsController < API::ApiController user = User.find(params[:user_id]) key = Setting.get('stripe_secret_key') @intent = Stripe::SetupIntent.create({ customer: user.stp_customer_id }, { api_key: key }) - render json: { client_secret: @intent.client_secret } + render json: { id: @intent.id, client_secret: @intent.client_secret } end def confirm_payment_schedule @@ -84,7 +84,7 @@ class API::PaymentsController < API::ApiController if params[:cart_items][:reservation] res = on_reservation_success(intent, amount[:details]) elsif params[:cart_items][:subscription] - res = on_subscription_success(intent) + res = on_subscription_success(intent, amount[:details]) end end @@ -103,7 +103,11 @@ class API::PaymentsController < API::ApiController current_user.id end is_reserve = Reservations::Reserve.new(user_id, current_user.invoicing_profile.id) - .pay_and_save(@reservation, payment_details: details, payment_intent_id: intent.id) + .pay_and_save(@reservation, + payment_details: details, + payment_intent_id: intent.id, + schedule: params[:cart_items][:reservation][:payment_schedule], + payment_method: params[:cart_items][:reservation][:payment_method]) if intent.class == Stripe::PaymentIntent Stripe::PaymentIntent.update( intent.id, @@ -121,7 +125,7 @@ class API::PaymentsController < API::ApiController end end - def on_subscription_success(intent) + def on_subscription_success(intent, details) @subscription = Subscription.new(subscription_params) user_id = if current_user.admin? || current_user.manager? params[:cart_items][:subscription][:user_id] @@ -130,8 +134,7 @@ class API::PaymentsController < API::ApiController end is_subscribe = Subscriptions::Subscribe.new(current_user.invoicing_profile.id, user_id) .pay_and_save(@subscription, - coupon: coupon_params[:coupon_code], - invoice: true, + payment_details: details, payment_intent_id: intent.id, schedule: params[:cart_items][:subscription][:payment_schedule], payment_method: 'stripe') diff --git a/app/controllers/api/reservations_controller.rb b/app/controllers/api/reservations_controller.rb index 549ee2a07..fd52e4d9e 100644 --- a/app/controllers/api/reservations_controller.rb +++ b/app/controllers/api/reservations_controller.rb @@ -35,7 +35,10 @@ class API::ReservationsController < API::ApiController @reservation = Reservation.new(reservation_params) is_reserve = Reservations::Reserve.new(user_id, current_user.invoicing_profile.id) - .pay_and_save(@reservation, payment_details: price[:price_details]) + .pay_and_save(@reservation, + payment_details: price[:price_details], + schedule: params[:reservation][:payment_schedule], + payment_method: params[:reservation][:payment_method]) if is_reserve SubscriptionExtensionAfterReservation.new(@reservation).extend_subscription_if_eligible diff --git a/app/controllers/api/subscriptions_controller.rb b/app/controllers/api/subscriptions_controller.rb index f79017acd..db8c1ce12 100644 --- a/app/controllers/api/subscriptions_controller.rb +++ b/app/controllers/api/subscriptions_controller.rb @@ -14,14 +14,13 @@ class API::SubscriptionsController < API::ApiController # Managers can create subscriptions for other users def create user_id = current_user.admin? || current_user.manager? ? params[:subscription][:user_id] : current_user.id - amount = transaction_amount(current_user.admin? || (current_user.manager? && current_user.id != user_id), user_id) + transaction = transaction_amount(current_user.admin? || (current_user.manager? && current_user.id != user_id), user_id) - authorize SubscriptionContext.new(Subscription, amount, user_id) + authorize SubscriptionContext.new(Subscription, transaction[:amount], user_id) @subscription = Subscription.new(subscription_params) is_subscribe = Subscriptions::Subscribe.new(current_user.invoicing_profile.id, user_id) - .pay_and_save(@subscription, coupon: coupon_params[:coupon_code], - invoice: true, + .pay_and_save(@subscription, payment_details: transaction[:details], schedule: params[:subscription][:payment_schedule], payment_method: params[:subscription][:payment_method]) @@ -65,7 +64,7 @@ class API::SubscriptionsController < API::ApiController # Subtract wallet amount from total total = price_details[:total] wallet_debit = get_wallet_debit(user, total) - total - wallet_debit + { amount: total - wallet_debit, details: price_details } end def get_wallet_debit(user, total_amount) diff --git a/app/frontend/src/javascript/api/api-client.ts b/app/frontend/src/javascript/api/api-client.ts index ad4aa60f2..eaead8232 100644 --- a/app/frontend/src/javascript/api/api-client.ts +++ b/app/frontend/src/javascript/api/api-client.ts @@ -19,6 +19,13 @@ client.interceptors.response.use(function (response) { }); function extractHumanReadableMessage(error: any): string { + if (error.match(/^/)) { + // parse ruby error pages + const parser = new DOMParser(); + const htmlDoc = parser.parseFromString(error, 'text/html'); + return htmlDoc.querySelector('h2').textContent; + } + if (typeof error === 'string') return error; let message = ''; diff --git a/app/frontend/src/javascript/controllers/admin/members.js b/app/frontend/src/javascript/controllers/admin/members.js index 483c6f325..3f105e484 100644 --- a/app/frontend/src/javascript/controllers/admin/members.js +++ b/app/frontend/src/javascript/controllers/admin/members.js @@ -37,7 +37,7 @@ */ class MembersController { constructor ($scope, $state, Group, Training) { - // Retrieve the profiles groups (eg. students ...) + // Retrieve the profiles groups (e.g. students ...) Group.query(function (groups) { $scope.groups = groups.filter(function (g) { return (g.slug !== 'admins') && !g.disabled; }); }); // Retrieve the list of available trainings @@ -62,7 +62,7 @@ class MembersController { }; /** - * Shows the birth day datepicker + * Shows the birthday datepicker * @param $event {Object} jQuery event object */ $scope.openDatePicker = function ($event) { @@ -85,7 +85,7 @@ class MembersController { * For use with ngUpload (https://github.com/twilson63/ngUpload). * Intended to be the callback when an upload is done: any raised error will be stacked in the * $scope.alerts array. If everything goes fine, the user is redirected to the members listing page. - * @param content {Object} JSON - The upload's result + * @param content {Object} JSON - The result of the upload */ $scope.submited = function (content) { if ((content.id == null)) { @@ -110,7 +110,7 @@ class MembersController { /** * For use with 'ng-class', returns the CSS class name for the uploads previews. - * The preview may show a placeholder or the content of the file depending on the upload state. + * The preview may show a placeholder, or the content of the file depending on the upload state. * @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules) */ $scope.fileinputClass = function (v) { @@ -143,7 +143,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', searchText: '', // Members ordering/sorting. Default: not sorted order: 'id', - // currently displayed page of members + // the currently displayed page of members page: 1, // true when all members where loaded noMore: false, @@ -158,7 +158,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', }; // admins list - $scope.admins = adminsPromise.admins.filter(function(m) { return m.id != Fablab.superadminId; }); + $scope.admins = adminsPromise.admins.filter(function (m) { return m.id !== Fablab.superadminId; }); // Admins ordering/sorting. Default: not sorted $scope.orderAdmin = null; @@ -210,7 +210,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', * @param orderPartner {string} ordering criterion */ $scope.setOrderPartner = function (orderPartner) { - if ($scope.orderPartner === orderPartner) { + if ($scope.orderPartner === orderPartner) { return $scope.orderPartner = `-${orderPartner}`; } else { return $scope.orderPartner = orderPartner; @@ -229,7 +229,6 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', } }; - /** * Open a modal dialog allowing the admin to create a new partner user */ @@ -265,12 +264,11 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', }); }; - /** * Ask for confirmation then delete the specified user * @param memberId {number} identifier of the user to delete */ - $scope.deleteMember = function(memberId) { + $scope.deleteMember = function (memberId) { dialogs.confirm( { resolve: { @@ -289,11 +287,14 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', $scope.members.splice(findItemIdxById($scope.members, memberId), 1); return growl.success(_t('app.admin.members.member_successfully_deleted')); }, - function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_member')); } + function (error) { + growl.error(_t('app.admin.members.unable_to_delete_the_member')); + console.error(error); + } ); } ); - } + }; /** * Ask for confirmation then delete the specified administrator @@ -319,7 +320,10 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', admins.splice(findItemIdxById(admins, admin.id), 1); return growl.success(_t('app.admin.members.administrator_successfully_deleted')); }, - function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_administrator')); } + function (error) { + growl.error(_t('app.admin.members.unable_to_delete_the_administrator')); + console.error(error); + } ); } ); @@ -349,11 +353,14 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', partners.splice(findItemIdxById(partners, partner.id), 1); return growl.success(_t('app.admin.members.partner_successfully_deleted')); }, - function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_partner')); } + function (error) { + growl.error(_t('app.admin.members.unable_to_delete_the_partner')); + console.error(error); + } ); } ); - } + }; /** * Ask for confirmation then delete the specified manager @@ -379,11 +386,14 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', managers.splice(findItemIdxById(managers, manager.id), 1); return growl.success(_t('app.admin.members.manager_successfully_deleted')); }, - function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_manager')); } + function (error) { + growl.error(_t('app.admin.members.unable_to_delete_the_manager')); + console.error(error); + } ); } ); - } + }; /** * Callback for the 'load more' button. @@ -399,7 +409,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', */ $scope.updateTextSearch = function () { if (searchTimeout) clearTimeout(searchTimeout); - searchTimeout = setTimeout(function() { + searchTimeout = setTimeout(function () { resetSearchMember(); memberSearch(); }, 300); @@ -425,9 +435,8 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', }); }; - /** - * Setup the feature-tour for the admin/members page. + * Set up the feature-tour for the admin/members page. * This is intended as a contextual help (when pressing F1) */ $scope.setupMembersTour = function () { @@ -570,7 +579,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', if (settingsPromise.feature_tour_display !== 'manual' && $scope.currentUser.profile.tours.indexOf('members') < 0) { uitour.start(); } - } + }; /* PRIVATE SCOPE */ @@ -586,22 +595,22 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', /** * Will temporize the search query to prevent overloading the API */ - var searchTimeout = null; + let searchTimeout = null; /** * Iterate through the provided array and return the index of the requested item - * @param items {Array} full list of users with role 'admin' + * @param items {Array} full list of users with the 'admin' role * @param id {Number} id of the item to retrieve in the list * @returns {Number} index of the requested item, in the provided array */ - var findItemIdxById = function (items, id) { + const findItemIdxById = function (items, id) { return (items.map(function (item) { return item.id; })).indexOf(id); }; /** - * Reinitialize the context of members's search to display new results set + * Reinitialize the context of the search to display new results set */ - var resetSearchMember = function () { + const resetSearchMember = function () { $scope.member.noMore = false; $scope.member.page = 1; }; @@ -609,9 +618,9 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', /** * Run a search query with the current parameters set ($scope.member[searchText,order,page]) * and affect or append the result in $scope.members, depending on the concat parameter - * @param [concat] {boolean} if true, the result will be append to $scope.members instead of being affected + * @param [concat] {boolean} if true, the result will be appended to $scope.members instead of being replaced */ - var memberSearch = function (concat) { + const memberSearch = function (concat) { Member.list({ query: { search: $scope.member.searchText, @@ -666,7 +675,6 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', // the user subscription if (($scope.user.subscribed_plan != null) && ($scope.user.subscription != null)) { $scope.subscription = $scope.user.subscription; - $scope.subscription.expired_at = $scope.subscription.expired_at; } else { Plan.query({ group_id: $scope.user.group_id }, function (plans) { $scope.plans = plans; @@ -696,16 +704,15 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', /** * Open a modal dialog asking for confirmation to change the role of the given user - * @param userId {number} id of the user to "promote" * @returns {*} */ - $scope.changeUserRole = function() { + $scope.changeUserRole = function () { const modalInstance = $uibModal.open({ animation: true, templateUrl: '/admin/members/change_role_modal.html', size: 'lg', resolve: { - user() { return $scope.user; } + user () { return $scope.user; } }, controller: ['$scope', '$uibModalInstance', 'Member', 'user', '_t', function ($scope, $uibModalInstance, Member, user, _t) { $scope.user = user; @@ -715,7 +722,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', $scope.roles = [ { key: 'admin', label: _t('app.admin.members_edit.admin') }, { key: 'manager', label: _t('app.admin.members_edit.manager'), notAnOption: (user.role === 'admin') }, - { key: 'member', label: _t('app.admin.members_edit.member'), notAnOption: (user.role === 'admin' || user.role === 'manager') }, + { key: 'member', label: _t('app.admin.members_edit.member'), notAnOption: (user.role === 'admin' || user.role === 'manager') } ]; $scope.ok = function () { @@ -740,7 +747,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', return modalInstance.result.then(function (user) { // remove the user for the old list add to the new }); - } + }; /** * Open a modal dialog, allowing the admin to extend the current user's subscription (freely or not) @@ -778,7 +785,10 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', growl.success(_t('app.admin.members_edit.you_successfully_changed_the_expiration_date_of_the_user_s_subscription')); return $uibModalInstance.close(_subscription); }, - function (error) { growl.error(_t('app.admin.members_edit.a_problem_occurred_while_saving_the_date')); } + function (error) { + growl.error(_t('app.admin.members_edit.a_problem_occurred_while_saving_the_date')); + console.error(error); + } ); }; @@ -792,14 +802,14 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', /** * Open a modal dialog allowing the admin to set a subscription for the given user. * @param user {Object} User object, user currently reviewed, as recovered from GET /api/members/:id - * @param plans {Array} List of plans, availables for the currently reviewed user, as recovered from GET /api/plans + * @param plans {Array} List of plans, available for the currently reviewed user, as recovered from GET /api/plans */ $scope.createSubscriptionModal = function (user, plans) { const modalInstance = $uibModal.open({ animation: true, templateUrl: '/admin/subscriptions/create_modal.html', size: 'lg', - controller: ['$scope', '$uibModalInstance', 'Subscription', 'Group', function ($scope, $uibModalInstance, Subscription, Group) { + controller: ['$scope', '$uibModalInstance', 'Subscription', function ($scope, $uibModalInstance, Subscription) { // selected user $scope.user = user; @@ -810,7 +820,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', * Generate a string identifying the given plan by literal human-readable name * @param plan {Object} Plan object, as recovered from GET /api/plan/:id * @param groups {Array} List of Groups objects, as recovered from GET /api/groups - * @param short {boolean} If true, the generated name will contains the group slug, otherwise the group full name + * @param short {boolean} If true, the generated name will contain the group slug, otherwise the group full name * will be included. * @returns {String} */ @@ -902,8 +912,9 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', */ $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); }; } - ] }); - // once the form was validated succesfully ... + ] + }); + // once the form was validated successfully... return modalInstance.result.then(function (wallet) { $scope.wallet = wallet; return Wallet.transactions({ id: wallet.id }, function (transactions) { $scope.transactions = transactions; }); @@ -923,13 +934,12 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', const initialize = function () { CSRF.setMetaTags(); - // init the birth date to JS object + // init the birthdate to JS object $scope.user.statistic_profile.birthday = moment($scope.user.statistic_profile.birthday).toDate(); // the user subscription if (($scope.user.subscribed_plan != null) && ($scope.user.subscription != null)) { $scope.subscription = $scope.user.subscription; - $scope.subscription.expired_at = $scope.subscription.expired_at; } else { Plan.query({ group_id: $scope.user.group_id }, function (plans) { $scope.plans = plans; @@ -996,7 +1006,7 @@ Application.Controllers.controller('NewMemberController', ['$scope', '$state', ' * Controller used in the member's import page: import from CSV (admin view) */ Application.Controllers.controller('ImportMembersController', ['$scope', '$state', 'Group', 'Training', 'CSRF', 'tags', 'growl', - function($scope, $state, Group, Training, CSRF, tags, growl) { + function ($scope, $state, Group, Training, CSRF, tags, growl) { CSRF.setMetaTags(); /* PUBLIC SCOPE */ @@ -1008,19 +1018,19 @@ Application.Controllers.controller('ImportMembersController', ['$scope', '$state $scope.method = 'post'; // List of all tags - $scope.tags = tags + $scope.tags = tags; /* * Callback run after the form was submitted - * @param content {*} The result provided by the server, may be an Import object or an error message + * @param content {*} The result provided by the server, may be an Import object, or an error message */ - $scope.onImportResult = function(content) { + $scope.onImportResult = function (content) { if (content.id) { $state.go('app.admin.members_import_result', { id: content.id }); } else { growl.error(JSON.stringify(content)); } - } + }; // Using the MembersController return new MembersController($scope, $state, Group, Training); @@ -1041,7 +1051,7 @@ Application.Controllers.controller('ImportMembersResultController', ['$scope', ' $scope.results = null; /** - * Changes the admin's view to the members import page + * Changes the view of the admin to the members import page */ $scope.cancel = function () { $state.go('app.admin.members_import'); }; @@ -1053,8 +1063,8 @@ Application.Controllers.controller('ImportMembersResultController', ['$scope', ' const initialize = function () { $scope.results = JSON.parse($scope.import.results); if (!$scope.results) { - setTimeout(function() { - Import.get({ id: $scope.import.id }, function(data) { + setTimeout(function () { + Import.get({ id: $scope.import.id }, function (data) { $scope.import = data; initialize(); }); @@ -1068,69 +1078,68 @@ Application.Controllers.controller('ImportMembersResultController', ['$scope', ' ]); /** - * Controller used in the admin's creation page (admin view) + * Controller used in the admin creation page (admin view) */ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'Admin', 'growl', '_t', 'phoneRequiredPromise', function ($state, $scope, Admin, growl, _t, phoneRequiredPromise) { // default admin profile - let getGender; - $scope.admin = { - statistic_profile_attributes: { - gender: true - }, - profile_attributes: {}, - invoicing_profile_attributes: {} - }; + let getGender; + $scope.admin = { + statistic_profile_attributes: { + gender: true + }, + profile_attributes: {}, + invoicing_profile_attributes: {} + }; - // Default parameters for AngularUI-Bootstrap datepicker - $scope.datePicker = { - format: Fablab.uibDateFormat, - opened: false, - options: { - startingDay: Fablab.weekStartingDay - } - }; + // Default parameters for AngularUI-Bootstrap datepicker + $scope.datePicker = { + format: Fablab.uibDateFormat, + opened: false, + options: { + startingDay: Fablab.weekStartingDay + } + }; - // is the phone number required in _admin_form? - $scope.phoneRequired = (phoneRequiredPromise.setting.value === 'true'); + // is the phone number required in _admin_form? + $scope.phoneRequired = (phoneRequiredPromise.setting.value === 'true'); - /** - * Shows the birth day datepicker - * @param $event {Object} jQuery event object + /** + * Shows the birthday datepicker */ - $scope.openDatePicker = function ($event) { $scope.datePicker.opened = true; }; + $scope.openDatePicker = function () { $scope.datePicker.opened = true; }; - /** + /** * Send the new admin, currently stored in $scope.admin, to the server for database saving */ - $scope.saveAdmin = function () { - Admin.save( - {}, - { admin: $scope.admin }, - function () { - growl.success(_t('app.admin.admins_new.administrator_successfully_created_he_will_receive_his_connection_directives_by_email', { GENDER: getGender($scope.admin) })); - return $state.go('app.admin.members'); - } - , function (error) { - growl.error(_t('app.admin.admins_new.failed_to_create_admin') + JSON.stringify(error.data ? error.data : error)); - console.error(error); - } - ); - }; + $scope.saveAdmin = function () { + Admin.save( + {}, + { admin: $scope.admin }, + function () { + growl.success(_t('app.admin.admins_new.administrator_successfully_created_he_will_receive_his_connection_directives_by_email', { GENDER: getGender($scope.admin) })); + return $state.go('app.admin.members'); + } + , function (error) { + growl.error(_t('app.admin.admins_new.failed_to_create_admin') + JSON.stringify(error.data ? error.data : error)); + console.error(error); + } + ); + }; - /* PRIVATE SCOPE */ + /* PRIVATE SCOPE */ - /** + /** * Return an enumerable meaningful string for the gender of the provider user * @param user {Object} Database user record * @return {string} 'male' or 'female' */ - return getGender = function (user) { - if (user.statistic_profile_attributes) { - if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; } - } else { return 'other'; } - }; -} + return getGender = function (user) { + if (user.statistic_profile_attributes) { + if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; } + } else { return 'other'; } + }; + } ]); @@ -1140,65 +1149,64 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A Application.Controllers.controller('NewManagerController', ['$state', '$scope', 'User', 'groupsPromise', 'tagsPromise', 'growl', '_t', function ($state, $scope, User, groupsPromise, tagsPromise, growl, _t) { // default admin profile - $scope.manager = { - statistic_profile_attributes: { - gender: true - }, - profile_attributes: {}, - invoicing_profile_attributes: {} - }; + $scope.manager = { + statistic_profile_attributes: { + gender: true + }, + profile_attributes: {}, + invoicing_profile_attributes: {} + }; - // Default parameters for AngularUI-Bootstrap datepicker - $scope.datePicker = { - format: Fablab.uibDateFormat, - opened: false, - options: { - startingDay: Fablab.weekStartingDay - } - }; + // Default parameters for AngularUI-Bootstrap datepicker + $scope.datePicker = { + format: Fablab.uibDateFormat, + opened: false, + options: { + startingDay: Fablab.weekStartingDay + } + }; - // list of all groups - $scope.groups = groupsPromise.filter(function (g) { return (g.slug !== 'admins') && !g.disabled; }); + // list of all groups + $scope.groups = groupsPromise.filter(function (g) { return (g.slug !== 'admins') && !g.disabled; }); - // list of all tags - $scope.tags = tagsPromise; + // list of all tags + $scope.tags = tagsPromise; - /** - * Shows the birth day datepicker - * @param $event {Object} jQuery event object + /** + * Shows the birthday datepicker */ - $scope.openDatePicker = function ($event) { $scope.datePicker.opened = true; }; + $scope.openDatePicker = function () { $scope.datePicker.opened = true; }; - /** + /** * Send the new manager, currently stored in $scope.manager, to the server for database saving */ - $scope.saveManager = function () { - User.save( - {}, - { manager: $scope.manager }, - function () { - growl.success(_t('app.admin.manager_new.manager_successfully_created', { GENDER: getGender($scope.manager) })); - return $state.go('app.admin.members'); - } - , function (error) { - growl.error(_t('app.admin.admins_new.failed_to_create_manager') + JSON.stringify(error.data ? error.data : error)); - console.error(error); - } - ); - }; + $scope.saveManager = function () { + User.save( + {}, + { manager: $scope.manager }, + function () { + growl.success(_t('app.admin.manager_new.manager_successfully_created', { GENDER: getGender($scope.manager) })); + return $state.go('app.admin.members'); + } + , function (error) { + growl.error(_t('app.admin.admins_new.failed_to_create_manager') + JSON.stringify(error.data ? error.data : error)); + console.error(error); + } + ); + }; - /* PRIVATE SCOPE */ + /* PRIVATE SCOPE */ - /** + /** * Return an enumerable meaningful string for the gender of the provider user * @param user {Object} Database user record * @return {string} 'male' or 'female' */ - const getGender = function (user) { - if (user.statistic_profile_attributes) { - if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; } - } else { return 'other'; } - }; -} + const getGender = function (user) { + if (user.statistic_profile_attributes) { + if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; } + } else { return 'other'; } + }; + } ]); diff --git a/app/frontend/src/javascript/directives/cart.js b/app/frontend/src/javascript/directives/cart.js index dea22cab0..a209e394a 100644 --- a/app/frontend/src/javascript/directives/cart.js +++ b/app/frontend/src/javascript/directives/cart.js @@ -716,9 +716,14 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs', * Open a modal window that allows the user to process a credit card payment for his current shopping cart. */ const payByStripe = function (reservation) { - $scope.toggleStripeModal(() => { - $scope.stripe.cartItems = mkCartItems(reservation, 'stripe'); - }); + // check that the online payment is enabled + if ($scope.settings.online_payment_module !== 'true') { + growl.error(_t('app.shared.cart.online_payment_disabled')); + } else { + $scope.toggleStripeModal(() => { + $scope.stripe.cartItems = mkCartItems(reservation, 'stripe'); + }); + } }; /** * Open a modal window that allows the user to process a local payment for his current shopping cart (admin only). @@ -751,10 +756,13 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs', }, user () { return $scope.user; + }, + settings () { + return $scope.settings; } }, - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'Subscription', 'wallet', 'helpers', '$filter', 'coupon', 'selectedPlan', 'schedule', 'cartItems', 'user', - function ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, Subscription, wallet, helpers, $filter, coupon, selectedPlan, schedule, cartItems, user) { + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'Subscription', 'wallet', 'helpers', '$filter', 'coupon', 'selectedPlan', 'schedule', 'cartItems', 'user', 'settings', + function ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, Subscription, wallet, helpers, $filter, coupon, selectedPlan, schedule, cartItems, user, settings) { // user wallet amount $scope.wallet = wallet; @@ -797,7 +805,12 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs', */ $scope.ok = function () { if ($scope.schedule && $scope.method.payment_method === 'stripe') { - return $scope.toggleStripeModal(); + // check that the online payment is enabled + if (settings.online_payment_module !== 'true') { + return growl.error(_t('app.shared.cart.online_payment_disabled')); + } else { + return $scope.toggleStripeModal(); + } } $scope.attempting = true; // save subscription (if there's only a subscription selected) @@ -927,11 +940,7 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs', const amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount); if ((AuthService.isAuthorized(['member']) && amountToPay > 0) || (AuthService.isAuthorized('manager') && $scope.user.id === $rootScope.currentUser.id && amountToPay > 0)) { - if ($scope.settings.online_payment_module !== 'true') { - growl.error(_t('app.shared.cart.online_payment_disabled')); - } else { - return payByStripe(reservation); - } + return payByStripe(reservation); } else { if (AuthService.isAuthorized(['admin']) || (AuthService.isAuthorized('manager') && $scope.user.id !== $rootScope.currentUser.id) || diff --git a/app/frontend/templates/admin/subscriptions/expired_at_modal.html b/app/frontend/templates/admin/subscriptions/expired_at_modal.html index 6d02cd49f..02e0b71ac 100644 --- a/app/frontend/templates/admin/subscriptions/expired_at_modal.html +++ b/app/frontend/templates/admin/subscriptions/expired_at_modal.html @@ -10,6 +10,7 @@
{{ 'app.admin.members_edit.you_intentionally_decide_to_extend_the_user_s_subscription_by_charging_him_again_for_his_current_subscription' }}
{{ 'app.admin.members_edit.credits_will_be_reset' }}
+{{ 'app.admin.members_edit.payment_scheduled' }}