1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-19 13:54:25 +01:00

[bug] subscription page shows the groups without any active plans

[bug] cart price inconsistently updated after a subscription
[feature] plans page for managers
This commit is contained in:
Sylvain 2020-04-29 10:57:32 +02:00
parent 05479e043c
commit 35ce577651
10 changed files with 58 additions and 32 deletions

View File

@ -8,6 +8,8 @@
- Fix a bug: "Free entry" label for events without reservation
- Fix a bug: updating a setting without any changes triggers an error
- Fix a bug: plan edition does not show the associated group
- Fix a bug: subscription page shows the groups without any active plans
- Fix a bug: cart price inconsistently updated after a subscription
## v4.3.4 2020 April 14

View File

@ -64,8 +64,6 @@ angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ui.rout
$translateProvider.useMessageFormatInterpolation();
// Set the language of the instance (from ruby configuration)
$translateProvider.preferredLanguage(Fablab.locale);
// In any cases, fallback to english
$translateProvider.fallbackLanguage('en');
// End the tour when the user clicks the forward or back buttons of the browser
TourConfigProvider.enableNavigationInterceptors();
}]).run(['$rootScope', '$log', 'AuthService', 'Auth', 'amMoment', '$state', 'editableOptions', 'Analytics',

View File

@ -12,8 +12,8 @@
*/
'use strict';
Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScope', '$state', '$uibModal', 'Auth', 'dialogs', 'growl', 'plansPromise', 'groupsPromise', 'Subscription', 'Member', 'subscriptionExplicationsPromise', '_t', 'Wallet', 'helpers',
function ($scope, $rootScope, $state, $uibModal, Auth, dialogs, growl, plansPromise, groupsPromise, Subscription, Member, subscriptionExplicationsPromise, _t, Wallet, helpers) {
Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScope', '$state', '$uibModal', 'Auth', 'AuthService', 'dialogs', 'growl', 'plansPromise', 'groupsPromise', 'Subscription', 'Member', 'subscriptionExplicationsPromise', '_t', 'Wallet', 'helpers',
function ($scope, $rootScope, $state, $uibModal, Auth, AuthService, dialogs, growl, plansPromise, groupsPromise, Subscription, Member, subscriptionExplicationsPromise, _t, Wallet, helpers) {
/* PUBLIC SCOPE */
// list of groups
@ -28,13 +28,6 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
// list of plans, classified by group
$scope.plansClassifiedByGroup = [];
for (const group of Array.from($scope.groups)) {
const groupObj = { id: group.id, name: group.name, plans: [] };
for (let plan of Array.from(plansPromise)) {
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
}
$scope.plansClassifiedByGroup.push(groupObj);
}
// user to deal with
$scope.ctrl = {
@ -62,7 +55,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
/**
* Callback to deal with the subscription of the user selected in the dropdown list instead of the current user's
* subscription. (admins only)
* subscription. (admins and managers only)
*/
$scope.updateMember = function () {
$scope.selectedPlan = null;
@ -97,14 +90,17 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
$scope.openSubscribePlanModal = function () {
Wallet.getWalletByUser({ user_id: $scope.ctrl.member.id }, function (wallet) {
const amountToPay = helpers.getAmountToPay($scope.cart.total, wallet.amount);
if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) {
if ((AuthService.isAuthorized('member') && amountToPay > 0)
|| (AuthService.isAuthorized('manager') && $scope.ctrl.member.id === $rootScope.currentUser.id && amountToPay > 0)) {
if ($rootScope.fablabWithoutOnlinePayment) {
growl.error(_t('app.public.plans.online_payment_disabled'));
} else {
return payByStripe();
}
} else {
if (($scope.currentUser.role === 'admin') || (amountToPay === 0)) {
if (AuthService.isAuthorized('admin')
|| (AuthService.isAuthorized('manager') && $scope.ctrl.member.id !== $rootScope.currentUser.id)
|| amountToPay === 0) {
return payOnSite();
}
}
@ -130,7 +126,8 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
$scope.ctrl.member = user;
$scope.group.change = false;
$scope.selectedPlan = null;
if ($scope.currentUser.role !== 'admin') {
if (AuthService.isAuthorized('member') ||
(AuthService.isAuthorized('manager') && $scope.currentUser.id !== $scope.ctrl.member.id)) {
$rootScope.currentUser = user;
Auth._currentUser.group_id = user.group_id;
growl.success(_t('app.public.plans.your_group_was_successfully_changed'));
@ -139,7 +136,8 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
}
}
, function (err) {
if ($scope.currentUser.role !== 'admin') {
if (AuthService.isAuthorized('member') ||
(AuthService.isAuthorized('manager') && $scope.currentUser.id !== $scope.ctrl.member.id)) {
growl.error(_t('app.public.plans.an_error_prevented_your_group_from_being_changed'));
} else {
growl.error(_t('app.public.plans.an_error_prevented_to_change_the_user_s_group'));
@ -179,8 +177,20 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
// group all plans by Group
for (const group of $scope.groups) {
const groupObj = { id: group.id, name: group.name, plans: [], actives: 0 };
for (let plan of plansPromise) {
if (plan.group_id === group.id) {
groupObj.plans.push(plan);
if (!plan.disabled) { groupObj.actives++; }
}
}
$scope.plansClassifiedByGroup.push(groupObj);
}
if ($scope.currentUser) {
if ($scope.currentUser.role !== 'admin') {
if (!AuthService.isAuthorized('admin')) {
$scope.ctrl.member = $scope.currentUser;
$scope.paid.plan = $scope.currentUser.subscribed_plan;
$scope.group.id = $scope.currentUser.group_id;
@ -201,9 +211,9 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
* Compute the total amount for the current reservation according to the previously set parameters
* and assign the result in $scope.reserve.amountTotal
*/
var updateCartPrice = function () {
// first we check that a user was selected
if (Object.keys($scope.ctrl.member).length > 0) {
const updateCartPrice = function () {
// first we check the selection of a user
if (Object.keys($scope.ctrl.member).length > 0 && $scope.selectedPlan) {
$scope.cart.total = $scope.selectedPlan.amount;
// apply the coupon if any
if ($scope.coupon.applied) {
@ -262,7 +272,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
CustomAsset.get({ name: 'cgv-file' }, function (cgv) { $scope.cgv = cgv.custom_asset; });
/**
* Callback for click on the 'proceed' button.
* Callback for a click on the 'proceed' button.
* Handle the stripe's card tokenization process response and save the subscription to the API with the
* card token just created.
*/
@ -301,7 +311,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
// user wallet amount
$scope.walletAmount = wallet.amount;
// subcription price, coupon subtracted if any
// subscription price, coupon subtracted if any
$scope.price = price;
// price to pay

View File

@ -17,7 +17,7 @@
<div class="row no-gutter">
<div class="col-sm-12 col-md-9 b-r">
<div class="row m-t m-b padder" ng-repeat="plansGroup in plansClassifiedByGroup | groupFilter:ctrl.member">
<div class="row m-t m-b padder" ng-repeat="plansGroup in plansClassifiedByGroup | groupFilter:ctrl.member" ng-show="plansGroup.actives > 0">
<div class="col-md-12 text-center">
<h2 class="text-u-c">{{plansGroup.name}}</h2>
@ -41,7 +41,7 @@
</div>
</div>
<div class="cta-button" ng-if="!ctrl.member || ctrl.member.role == 'member'">
<div class="cta-button" ng-if="!ctrl.member || ctrl.member.role == 'member' || (ctrl.member.role == 'manager' && ctrl.member.id === currentUser.id)">
<button class="btn btn-default rounded" ng-click="selectPlan(plan)" ng-if="ctrl.member.subscribed_plan.id != plan.id" ng-disabled="ctrl.member.subscribed_plan" ng-class="{ 'bg-yellow': selectedPlan==plan }">
<span ng-if="ctrl.member" translate>{{ 'app.public.plans.i_choose_that_plan' }}</span>
<span ng-if="!ctrl.member" translate>{{ 'app.public.plans.i_subscribe_online' }}</span>
@ -53,7 +53,7 @@
</div>
<div class="cta-button" ng-if="isAuthorized(['admin', 'manager'])">
<div class="cta-button" ng-if="isAuthorized('admin') || (ctrl.member.role == 'manager' && ctrl.member.id != currentUser.id)">
<button class="btn btn-default rounded" ng-click="selectPlan(plan)" ng-class="{ 'bg-yellow': selectedPlan==plan }" ng-disabled="!ctrl.member">
<span translate>{{ 'app.public.plans.i_choose_that_plan' }}</span>
</button>

View File

@ -10,12 +10,13 @@ class API::SubscriptionsController < API::ApiController
end
# Admins can create any subscriptions. Members can directly create subscriptions if total = 0,
# otherwise, they must use payments_controller#confirm_payment
# otherwise, they must use payments_controller#confirm_payment.
# Managers can create subscriptions for other users
def create
user_id = current_user.admin? ? params[:subscription][:user_id] : current_user.id
user_id = current_user.admin? || current_user.manager? ? params[:subscription][:user_id] : current_user.id
amount = transaction_amount(current_user.admin?, user_id)
authorize SubscriptionContext.new(Subscription, amount)
authorize SubscriptionContext.new(Subscription, amount, user_id)
@subscription = Subscription.new(subscription_params)
is_subscribe = Subscriptions::Subscribe.new(current_user.invoicing_profile.id, user_id)

View File

@ -105,6 +105,10 @@ class User < ApplicationRecord
User.with_role(:manager)
end
def self.online_payers
User.with_any_role(:manager, :member)
end
def self.superadmin
return unless Rails.application.secrets.superadmin_email.present?

View File

@ -2,11 +2,12 @@
# Pundit Additional context to validate the price of a subscription
class SubscriptionContext
attr_reader :subscription, :price
attr_reader :subscription, :price, :user_id
def initialize(subscription, price)
def initialize(subscription, price, user_id)
@subscription = subscription
@price = price
@user_id = user_id
end
def policy_class

View File

@ -4,7 +4,7 @@
class SubscriptionPolicy < ApplicationPolicy
include FablabConfiguration
def create?
!fablab_plans_deactivated? && (user.admin? || record.price.zero?)
!fablab_plans_deactivated? && (user.admin? || (user.manager? && record.user_id != user.id) || record.price.zero?)
end
def show?

View File

@ -29,6 +29,7 @@ fr:
#left menu
notifications: "Notifications"
admin: "Admin"
manager: "Gestionnaire"
reduce_panel: "Réduire le volet"
#left menu (public)
home: "Accueil"

View File

@ -48,7 +48,10 @@ namespace :fablab do
desc 'sync users to the stripe database'
task sync_members: :environment do
User.members.each do |member|
puts 'We create all non-existing customers on stripe. This may take a while, please wait...'
total = User.online_payers.count
User.online_payers.each_with_index do |member, index|
print_on_line "#{index} / #{total}"
begin
stp_customer = Stripe::Customer.retrieve member.stp_customer_id
StripeWorker.perform_async(:create_stripe_customer, member.id) if stp_customer.nil? || stp_customer[:deleted]
@ -56,6 +59,12 @@ namespace :fablab do
StripeWorker.perform_async(:create_stripe_customer, member.id)
end
end
puts 'Done'
end
def print_on_line(str)
print "#{str}\r"
$stdout.flush
end
end
end