1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-20 14:54:15 +01:00

(feat) cancel subscription

This commit is contained in:
Sylvain 2023-01-03 17:17:39 +01:00
parent 375f0bd137
commit 43f45c383f
16 changed files with 158 additions and 11 deletions

View File

@ -9,6 +9,7 @@
- Accounting data is now built each night and saved in database
- Ability to define multiple accounting journal codes
- Ability to change the name of the VAT
- Ability to cancel a running subscription from the member edition view for admin/managers
- OpenAPI endpoint to fetch accounting data
- Add reservation deadline parameter (#414)
- Verify current password at server side when changing password

View File

@ -2,7 +2,7 @@
# API Controller for resources of type Subscription
class API::SubscriptionsController < API::ApiController
before_action :set_subscription, only: %i[show payment_details]
before_action :set_subscription, only: %i[show payment_details cancel]
before_action :authenticate_user!
def show
@ -13,6 +13,15 @@ class API::SubscriptionsController < API::ApiController
authorize @subscription
end
def cancel
authorize @subscription
if @subscription.expire(DateTime.current)
render :show, status: :ok, location: @subscription
else
render json: { error: 'already expired' }, status: :unprocessable_entity
end
end
private
# Use callbacks to share common setup or constraints between actions.

View File

@ -1,10 +1,10 @@
import apiClient from './clients/api-client';
import { Subscription, SubscriptionPaymentDetails, UpdateSubscriptionRequest } from '../models/subscription';
import { Subscription, SubscriptionPaymentDetails } from '../models/subscription';
import { AxiosResponse } from 'axios';
export default class SubscriptionAPI {
static async update (request: UpdateSubscriptionRequest): Promise<Subscription> {
const res: AxiosResponse<Subscription> = await apiClient.patch(`/api/subscriptions/${request.id}`, { subscription: request });
static async cancel (id: number): Promise<Subscription> {
const res: AxiosResponse<Subscription> = await apiClient.patch(`/api/subscriptions/${id}/cancel`);
return res?.data;
}

View File

@ -0,0 +1,57 @@
import { IApplication } from '../../models/application';
import { Subscription } from '../../models/subscription';
import { FabModal } from '../base/fab-modal';
import { Loader } from '../base/loader';
import { react2angular } from 'react2angular';
import * as React from 'react';
import SubscriptionAPI from '../../api/subscription';
import { useTranslation } from 'react-i18next';
import { HtmlTranslate } from '../base/html-translate';
declare const Application: IApplication;
interface CancelSubscriptionModalProps {
isOpen: boolean,
toggleModal: () => void,
subscription: Subscription,
onSuccess: (message: string) => void,
onError: (message: string) => void,
}
/**
* Modam dialog shown to confirm the cancelation of the current running subscription of a customer
*/
export const CancelSubscriptionModal: React.FC<CancelSubscriptionModalProps> = ({ isOpen, toggleModal, subscription, onError, onSuccess }) => {
const { t } = useTranslation('admin');
/**
* Callback triggered when the user has confirmed the cancelation of the subscription
*/
const handleCancelConfirmed = () => {
SubscriptionAPI.cancel(subscription.id).then(() => {
toggleModal();
onSuccess(t('app.admin.cancel_subscription_modal.subscription_canceled'));
}).catch(onError);
};
return (
<FabModal isOpen={isOpen}
toggleModal={toggleModal}
confirmButton={t('app.admin.cancel_subscription_modal.confirm')}
title={t('app.admin.cancel_subscription_modal.title')}
onConfirm={handleCancelConfirmed}
closeButton>
<HtmlTranslate trKey={'app.admin.cancel_subscription_modal.confirmation_html'} options={{ NAME: subscription.plan.base_name }} />
</FabModal>
);
};
const CancelSubscriptionModalWrapper: React.FC<CancelSubscriptionModalProps> = (props) => {
return (
<Loader>
<CancelSubscriptionModal {...props} />
</Loader>
);
};
Application.Components.component('cancelSubscriptionModal', react2angular(CancelSubscriptionModalWrapper, ['toggleModal', 'subscription', 'isOpen', 'onError', 'onSuccess']));

View File

@ -727,6 +727,9 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
// modal dialog to change the user's role
$scope.isOpenChangeRoleModal = false;
// modal dialog to cancel the current subscription
$scope.isOpenCancelModal = false;
/**
* Open a modal dialog asking for confirmation to change the role of the given user
* @returns {*}
@ -794,6 +797,16 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
}, 50);
};
/**
* Opens/closes the modal dialog to cancel the current running subscription
*/
$scope.toggleCancelModal = () => {
setTimeout(() => {
$scope.isOpenCancelModal = !$scope.isOpenCancelModal;
$scope.$apply();
}, 50);
};
/**
* Opens/closes the modal dialog to renew the subscription (with payment)
*/
@ -822,6 +835,16 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
$scope.subscription.expired_at = newExpirationDate;
};
/**
* Callback triggered when the subscription was successfully canceled
*/
$scope.onCancelSuccess = (message) => {
growl.success(message);
$scope.user.subscribed_plan = null;
$scope.user.subscription = null;
$scope.subscription = null;
};
/**
* Callback triggered if a new subscription was successfully taken
*/

View File

@ -14,12 +14,6 @@ export interface SubscriptionRequest {
start_at?: TDateISO
}
export interface UpdateSubscriptionRequest {
id: number,
expired_at: TDateISO,
free: boolean
}
export interface SubscriptionPaymentDetails {
payment_schedule: boolean,
card: boolean

View File

@ -75,7 +75,7 @@
<section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r">
<div class="" ng-show="subscription">
<div class="" ng-if="subscription">
<h3>{{ subscription.plan | humanReadablePlanName }}</h3>
<p>
{{ 'app.admin.members_edit.duration' | translate }} {{ subscription.plan.interval | planIntervalFilter: subscription.plan.interval_count }}
@ -89,6 +89,7 @@
<div ng-hide="user.id === currentUser.id">
<button class="btn btn-default" ng-click="toggleFreeExtendModal()" translate>{{ 'app.admin.members_edit.offer_free_days' }}</button>
<button class="btn btn-default" ng-click="toggleRenewModal()" translate>{{ 'app.admin.members_edit.renew_subscription' }}</button>
<button class="btn btn-default" ng-click="toggleCancelModal()" translate>{{ 'app.admin.members_edit.cancel_subscription' }}</button>
<free-extend-modal is-open="isOpenFreeExtendModal"
toggle-modal="toggleFreeExtendModal"
subscription="subscription"
@ -104,6 +105,14 @@
on-error="onError"
on-success="onExtendSuccess">
</renew-modal>
<cancel-subscription-modal is-open="isOpenCancelModal"
toggle-modal="toggleCancelModal"
subscription="subscription"
customer="user"
operator="currentUser"
on-error="onError"
on-success="onCancelSuccess">
</cancel-subscription-modal>
</div>
<p class="alert alert-info" ng-show="user.id === currentUser.id" translate>
{{ 'app.admin.members_edit.cannot_extend_own_subscription' }}

View File

@ -9,4 +9,8 @@ class SubscriptionPolicy < ApplicationPolicy
def payment_details?
user.admin? || user.manager?
end
def cancel?
user.admin? || user.manager?
end
end

View File

@ -1134,6 +1134,7 @@ de:
price_: "Preis:"
offer_free_days: "Kostenlose Tage anbieten"
renew_subscription: "Abonnement erneuern"
cancel_subscription: "Cancel the subscription"
user_has_no_current_subscription: "Benutzer hat kein aktuelles Abonnement."
subscribe_to_a_plan: "Plan abonnieren"
trainings: "Schulungen"
@ -1208,6 +1209,12 @@ de:
select_plan: "Bitte wählen Sie einen Plan"
pay_in_one_go: "In einem Schritt bezahlen"
subscription_success: "Subscription successfully subscribed"
#cancel the current subscription
cancel_subscription_modal:
title: "Confirmation required"
confirmation_html: "You are about to cancel the subscription <em>{NAME}</em> of this user. From now, he won't be able to benefit from the advantages of this subscription, and all his unused credits will be lost. <strong>Are your sure?</strong>"
confirm: "Cancel this subscription"
subscription_canceled: "The subscription was successfully canceled."
#add a new administrator to the platform
admins_new:
add_an_administrator: "Administrator hinzufügen"

View File

@ -1134,6 +1134,7 @@ en:
price_: "Price:"
offer_free_days: "Offer free days"
renew_subscription: "Renew the subscription"
cancel_subscription: "Cancel the subscription"
user_has_no_current_subscription: "User has no current subscription."
subscribe_to_a_plan: "Subscribe to a plan"
trainings: "Trainings"
@ -1208,6 +1209,12 @@ en:
select_plan: "Please select a plan"
pay_in_one_go: "Pay in one go"
subscription_success: "Subscription successfully subscribed"
# cancel the current subscription
cancel_subscription_modal:
title: "Confirmation required"
confirmation_html: "You are about to cancel the subscription <em>{NAME}</em> of this user. From now, he won't be able to benefit from the advantages of this subscription, and all his unused credits will be lost. <strong>Are your sure?</strong>"
confirm: "Cancel this subscription"
subscription_canceled: "The subscription was successfully canceled."
#add a new administrator to the platform
admins_new:
add_an_administrator: "Add an administrator"

View File

@ -1134,6 +1134,7 @@ es:
price_: "Precio:"
offer_free_days: "Ofrecer días gratis"
renew_subscription: "Renew the subscription"
cancel_subscription: "Cancel the subscription"
user_has_no_current_subscription: "El usuario no tiene una suscripción actual."
subscribe_to_a_plan: "Suscribirse a un plan"
trainings: "Trainings"
@ -1208,6 +1209,12 @@ es:
select_plan: "Please select a plan"
pay_in_one_go: "Pay in one go"
subscription_success: "Subscription successfully subscribed"
#cancel the current subscription
cancel_subscription_modal:
title: "Confirmation required"
confirmation_html: "You are about to cancel the subscription <em>{NAME}</em> of this user. From now, he won't be able to benefit from the advantages of this subscription, and all his unused credits will be lost. <strong>Are your sure?</strong>"
confirm: "Cancel this subscription"
subscription_canceled: "The subscription was successfully canceled."
#add a new administrator to the platform
admins_new:
add_an_administrator: "Agregar un administrador"

View File

@ -1134,6 +1134,7 @@ fr:
price_: "Prix :"
offer_free_days: "Offrir des jours gratuits"
renew_subscription: "Renouveler l'abonnement"
cancel_subscription: "Annuler l'abonnement"
user_has_no_current_subscription: "L'utilisateur n'a pas d'abonnement en cours."
subscribe_to_a_plan: "Souscrire à un abonnement"
trainings: "Formations"
@ -1208,6 +1209,12 @@ fr:
select_plan: "Veuillez choisir une formule d'abonnement"
pay_in_one_go: "Payer en une fois"
subscription_success: "Abonnement souscrit avec succès"
#cancel the current subscription
cancel_subscription_modal:
title: "Confirmation requise"
confirmation_html: "Vous êtes sur le point d'annuler l'abonnement <em>{NAME}</em> de cet utilisateur. À partir de maintenant, il ne pourra plus bénéficier des avantages de cet abonnement et tous ses crédits inutilisés seront perdus. <strong>Êtes-vous sûr(e) ?</strong>"
confirm: "Annuler cet abonnement"
subscription_canceled: "L'abonnement a bien été annulé."
#add a new administrator to the platform
admins_new:
add_an_administrator: "Ajouter un administrateur"

View File

@ -1134,6 +1134,7 @@
price_: "Pris:"
offer_free_days: "Tilby gratis dager"
renew_subscription: "Forny abonnementet"
cancel_subscription: "Cancel the subscription"
user_has_no_current_subscription: "Brukeren har ikke noe gjeldende medlemskap."
subscribe_to_a_plan: "Abonner på et medlemskap"
trainings: "Opplæringer/kurs"
@ -1208,6 +1209,12 @@
select_plan: "Please select a plan"
pay_in_one_go: "Pay in one go"
subscription_success: "Subscription successfully subscribed"
#cancel the current subscription
cancel_subscription_modal:
title: "Confirmation required"
confirmation_html: "You are about to cancel the subscription <em>{NAME}</em> of this user. From now, he won't be able to benefit from the advantages of this subscription, and all his unused credits will be lost. <strong>Are your sure?</strong>"
confirm: "Cancel this subscription"
subscription_canceled: "The subscription was successfully canceled."
#add a new administrator to the platform
admins_new:
add_an_administrator: "Legg til administrator"

View File

@ -1134,6 +1134,7 @@ pt:
price_: "Preço:"
offer_free_days: "Oferecer dias grátis"
renew_subscription: "Renovar assinatura"
cancel_subscription: "Cancel the subscription"
user_has_no_current_subscription: "O usuário não possui inscrição."
subscribe_to_a_plan: "Plano de inscrição"
trainings: "Treinamentos"
@ -1208,6 +1209,12 @@ pt:
select_plan: "Por favor, selecione um plano"
pay_in_one_go: "Paga á vista"
subscription_success: "Assinatura assinada com sucesso"
#cancel the current subscription
cancel_subscription_modal:
title: "Confirmation required"
confirmation_html: "You are about to cancel the subscription <em>{NAME}</em> of this user. From now, he won't be able to benefit from the advantages of this subscription, and all his unused credits will be lost. <strong>Are your sure?</strong>"
confirm: "Cancel this subscription"
subscription_canceled: "The subscription was successfully canceled."
#add a new administrator to the platform
admins_new:
add_an_administrator: "Adicionar administrador"

View File

@ -1134,6 +1134,7 @@ zu:
price_: "crwdns25854:0crwdne25854:0"
offer_free_days: "crwdns25856:0crwdne25856:0"
renew_subscription: "crwdns25858:0crwdne25858:0"
cancel_subscription: "crwdns36255:0crwdne36255:0"
user_has_no_current_subscription: "crwdns25860:0crwdne25860:0"
subscribe_to_a_plan: "crwdns25862:0crwdne25862:0"
trainings: "crwdns25864:0crwdne25864:0"
@ -1208,6 +1209,12 @@ zu:
select_plan: "crwdns25988:0crwdne25988:0"
pay_in_one_go: "crwdns25990:0crwdne25990:0"
subscription_success: "crwdns25992:0crwdne25992:0"
#cancel the current subscription
cancel_subscription_modal:
title: "crwdns36257:0crwdne36257:0"
confirmation_html: "crwdns36259:0{NAME}crwdne36259:0"
confirm: "crwdns36261:0crwdne36261:0"
subscription_canceled: "crwdns36263:0crwdne36263:0"
#add a new administrator to the platform
admins_new:
add_an_administrator: "crwdns25994:0crwdne25994:0"

View File

@ -108,6 +108,7 @@ Rails.application.routes.draw do
resources :groups, only: %i[index create update destroy]
resources :subscriptions, only: %i[show] do
get 'payment_details', action: 'payment_details', on: :member
patch 'cancel', on: :member
end
resources :plan_categories
resources :plans do