mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-17 06:52:27 +01:00
refactored free subscription extending
This commit is contained in:
parent
70f0e21543
commit
17a0baac7e
@ -12,10 +12,8 @@ class API::SubscriptionsController < API::ApiController
|
||||
def update
|
||||
authorize @subscription
|
||||
|
||||
free_days = params[:subscription][:free] || false
|
||||
|
||||
res = Subscriptions::Subscribe.new(current_user.invoicing_profile.id)
|
||||
.extend_subscription(@subscription, subscription_update_params[:expired_at], free_days)
|
||||
.extend_subscription(@subscription, subscription_update_params[:expired_at])
|
||||
if res.is_a?(Subscription)
|
||||
@subscription = res
|
||||
render status: :created
|
||||
|
@ -8,7 +8,8 @@ import FormatLib from '../../lib/format';
|
||||
import { Loader } from '../base/loader';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { IApplication } from '../../models/application';
|
||||
import SubscriptionAPI from '../../api/subscription';
|
||||
import LocalPaymentAPI from '../../api/local-payment';
|
||||
import { PaymentMethod } from '../../models/payment';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
@ -16,14 +17,15 @@ interface FreeExtendModalProps {
|
||||
isOpen: boolean,
|
||||
toggleModal: () => void,
|
||||
subscription: Subscription,
|
||||
onSuccess: (subscription: Subscription) => void,
|
||||
customerId: number,
|
||||
onSuccess: (message: string, newExpirationDate: Date) => void,
|
||||
onError: (message: string) => void,
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal dialog shown to extend the current subscription of a customer, for free
|
||||
*/
|
||||
const FreeExtendModal: React.FC<FreeExtendModalProps> = ({ isOpen, toggleModal, subscription, onError, onSuccess }) => {
|
||||
const FreeExtendModal: React.FC<FreeExtendModalProps> = ({ isOpen, toggleModal, subscription, customerId, onError, onSuccess }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const [expirationDate, setExpirationDate] = useState<Date>(new Date(subscription.expired_at));
|
||||
@ -63,12 +65,20 @@ const FreeExtendModal: React.FC<FreeExtendModalProps> = ({ isOpen, toggleModal,
|
||||
* Callback triggered when the user validates the free extent of the subscription
|
||||
*/
|
||||
const handleConfirmExtend = (): void => {
|
||||
SubscriptionAPI.update({
|
||||
id: subscription.id,
|
||||
expired_at: expirationDate,
|
||||
free: true
|
||||
}).then(res => onSuccess(res))
|
||||
.catch(err => onError(err));
|
||||
LocalPaymentAPI.confirmPayment({
|
||||
customer_id: customerId,
|
||||
payment_method: PaymentMethod.Other,
|
||||
items: [
|
||||
{
|
||||
free_extension: {
|
||||
end_at: expirationDate
|
||||
}
|
||||
}
|
||||
]
|
||||
}).then(() => {
|
||||
onSuccess(t('app.admin.free_extend_modal.extend_success'), expirationDate);
|
||||
toggleModal();
|
||||
}).catch(err => onError(err));
|
||||
};
|
||||
|
||||
return (
|
||||
@ -101,12 +111,12 @@ const FreeExtendModal: React.FC<FreeExtendModalProps> = ({ isOpen, toggleModal,
|
||||
);
|
||||
};
|
||||
|
||||
const FreeExtendModalWrapper: React.FC<FreeExtendModalProps> = ({ toggleModal, subscription, isOpen, onSuccess, onError }) => {
|
||||
const FreeExtendModalWrapper: React.FC<FreeExtendModalProps> = ({ toggleModal, subscription, customerId, isOpen, onSuccess, onError }) => {
|
||||
return (
|
||||
<Loader>
|
||||
<FreeExtendModal toggleModal={toggleModal} subscription={subscription} isOpen={isOpen} onError={onError} onSuccess={onSuccess} />
|
||||
<FreeExtendModal toggleModal={toggleModal} subscription={subscription} customerId={customerId} isOpen={isOpen} onError={onError} onSuccess={onSuccess} />
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('freeExtendModal', react2angular(FreeExtendModalWrapper, ['toggleModal', 'subscription', 'isOpen', 'onError', 'onSuccess']));
|
||||
Application.Components.component('freeExtendModal', react2angular(FreeExtendModalWrapper, ['toggleModal', 'subscription', 'customerId', 'isOpen', 'onError', 'onSuccess']));
|
||||
|
@ -759,14 +759,18 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
|
||||
* Opens/closes the modal dialog to freely extend the subscription
|
||||
*/
|
||||
$scope.toggleFreeExtendModal = () => {
|
||||
setTimeout(() => {
|
||||
$scope.isOpenFreeExtendModal = !$scope.isOpenFreeExtendModal;
|
||||
$scope.$apply();
|
||||
}, 50);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered if the subscription was successfully extended
|
||||
*/
|
||||
$scope.onExtendSuccess = (subscription) => {
|
||||
$scope.subscription = subscription;
|
||||
$scope.onExtendSuccess = (message, newExpirationDate) => {
|
||||
growl.success(message);
|
||||
$scope.subscription.expired_at = newExpirationDate;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,10 @@ export enum PaymentMethod {
|
||||
Other = ''
|
||||
}
|
||||
|
||||
export type CartItem = { reservation: Reservation }|{ subscription: SubscriptionRequest }|{ prepaid_pack: { id: number } };
|
||||
export type CartItem = { reservation: Reservation }|
|
||||
{ subscription: SubscriptionRequest }|
|
||||
{ prepaid_pack: { id: number } }|
|
||||
{ free_extension: { end_at: Date } };
|
||||
|
||||
export interface ShoppingCart {
|
||||
customer_id: number,
|
||||
|
@ -82,6 +82,7 @@
|
||||
<free-extend-modal is-open="isOpenFreeExtendModal"
|
||||
toggle-modal="toggleFreeExtendModal"
|
||||
subscription="subscription"
|
||||
customer-id="user.id"
|
||||
on-error="onError"
|
||||
on-success="onExtendSuccess">
|
||||
</free-extend-modal>
|
||||
|
38
app/models/cart_item/free_extension.rb
Normal file
38
app/models/cart_item/free_extension.rb
Normal file
@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# A subscription extended for free, added to the shopping cart
|
||||
class CartItem::FreeExtension < CartItem::BaseItem
|
||||
def initialize(customer, subscription, new_expiration_date)
|
||||
raise TypeError unless subscription.is_a? Subscription
|
||||
|
||||
@customer = customer
|
||||
@new_expiration_date = new_expiration_date
|
||||
@subscription = subscription
|
||||
super
|
||||
end
|
||||
|
||||
def start_at
|
||||
raise InvalidSubscriptionError if @subscription.nil?
|
||||
raise InvalidSubscriptionError if @new_expiration_date <= @subscription.expired_at
|
||||
|
||||
@subscription.expired_at
|
||||
end
|
||||
|
||||
def price
|
||||
elements = { OfferDay: 0 }
|
||||
|
||||
{ elements: elements, amount: 0 }
|
||||
end
|
||||
|
||||
def name
|
||||
I18n.t('cart_items.free_extension', DATE: I18n.l(@new_expiration_date))
|
||||
end
|
||||
|
||||
def to_object
|
||||
::OfferDay.new(
|
||||
subscription_id: @subscription.id,
|
||||
start_at: start_at,
|
||||
end_at: @new_expiration_date
|
||||
)
|
||||
end
|
||||
end
|
@ -7,6 +7,8 @@ class OfferDay < ApplicationRecord
|
||||
has_many :invoice_items, as: :object, dependent: :destroy
|
||||
belongs_to :subscription
|
||||
|
||||
after_create :notify_subscription_extended
|
||||
|
||||
# buying invoice
|
||||
def original_invoice
|
||||
invoice_items.select(:invoice_id)
|
||||
@ -15,4 +17,20 @@ class OfferDay < ApplicationRecord
|
||||
.map { |id| Invoice.find_by(id: id, type: nil) }
|
||||
.first
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def notify_subscription_extended
|
||||
meta_data = { free_days: true }
|
||||
NotificationCenter.call type: :notify_member_subscription_extended,
|
||||
receiver: subscription.user,
|
||||
attached_object: subscription,
|
||||
meta_data: meta_data
|
||||
|
||||
NotificationCenter.call type: :notify_admin_subscription_extended,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: subscription,
|
||||
meta_data: meta_data
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -122,6 +122,7 @@ class Setting < ApplicationRecord
|
||||
renew_pack_threshold
|
||||
pack_only_for_subscription] }
|
||||
# WARNING: when adding a new key, you may also want to add it in app/policies/setting_policy.rb#public_whitelist
|
||||
# and in config/locales/en.yml#settings
|
||||
|
||||
def value
|
||||
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(1).first
|
||||
|
@ -47,26 +47,6 @@ class Subscription < ApplicationRecord
|
||||
expiration_date
|
||||
end
|
||||
|
||||
def free_extend(expiration, operator_profile_id)
|
||||
return false if expiration <= expired_at
|
||||
|
||||
od = offer_days.create(start_at: expired_at, end_at: expiration)
|
||||
invoice = Invoice.new(
|
||||
invoicing_profile: user.invoicing_profile,
|
||||
statistic_profile: user.statistic_profile,
|
||||
operator_profile_id: operator_profile_id,
|
||||
total: 0
|
||||
)
|
||||
invoice.invoice_items.push InvoiceItem.new(amount: 0, description: plan.base_name, object: od)
|
||||
invoice.save
|
||||
|
||||
if save
|
||||
notify_subscription_extended(true)
|
||||
return true
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def user
|
||||
statistic_profile.user
|
||||
end
|
||||
@ -116,9 +96,8 @@ class Subscription < ApplicationRecord
|
||||
attached_object: self
|
||||
end
|
||||
|
||||
def notify_subscription_extended(free_days)
|
||||
meta_data = {}
|
||||
meta_data[:free_days] = true if free_days
|
||||
def notify_subscription_extended
|
||||
meta_data = { free_days: false }
|
||||
NotificationCenter.call type: :notify_member_subscription_extended,
|
||||
receiver: user,
|
||||
attached_object: self,
|
||||
|
@ -3,6 +3,9 @@
|
||||
# Check the access policies for API::LocalPaymentsController
|
||||
class LocalPaymentPolicy < ApplicationPolicy
|
||||
def confirm_payment?
|
||||
user.admin? || (user.manager? && record.shopping_cart.customer.id != user.id) || record.price.zero?
|
||||
# only admins and managers can offer free extensions of a subscription
|
||||
has_free_days = record.shopping_cart.items.any? { |item| item.is_a? CartItem::FreeExtension }
|
||||
|
||||
user.admin? || (user.manager? && record.shopping_cart.customer.id != user.id) || (record.price.zero? && !has_free_days)
|
||||
end
|
||||
end
|
||||
|
@ -22,6 +22,8 @@ class CartService
|
||||
items.push(reservable_from_hash(item[:reservation], plan_info))
|
||||
elsif ['prepaid_pack', :prepaid_pack].include?(item.keys.first)
|
||||
items.push(CartItem::PrepaidPack.new(PrepaidPack.find(item[:prepaid_pack][:id]), @customer))
|
||||
elsif ['free_extension', :free_extension].include?(item.keys.first)
|
||||
items.push(CartItem::FreeExtension.new(@customer, plan_info[:subscription], item[:free_extension][:end_at]))
|
||||
end
|
||||
end
|
||||
|
||||
@ -70,18 +72,21 @@ class CartService
|
||||
|
||||
def plan(cart_items)
|
||||
new_plan_being_bought = false
|
||||
subscription = nil
|
||||
plan = if cart_items[:items].any? { |item| ['subscription', :subscription].include?(item.keys.first) }
|
||||
index = cart_items[:items].index { |item| ['subscription', :subscription].include?(item.keys.first) }
|
||||
if cart_items[:items][index][:subscription][:plan_id]
|
||||
new_plan_being_bought = true
|
||||
subscription = cart_items[:items][index][:subscription].to_object
|
||||
Plan.find(cart_items[:items][index][:subscription][:plan_id])
|
||||
end
|
||||
elsif @customer.subscribed_plan
|
||||
subscription = @customer.subscription unless @customer.subscription.expired_at < DateTime.current
|
||||
@customer.subscribed_plan
|
||||
else
|
||||
nil
|
||||
end
|
||||
{ plan: plan, new_subscription: new_plan_being_bought }
|
||||
{ plan: plan, subscription: subscription, new_subscription: new_plan_being_bought }
|
||||
end
|
||||
|
||||
def customer(cart_items)
|
||||
|
@ -93,7 +93,7 @@ class InvoicesService
|
||||
end
|
||||
|
||||
##
|
||||
# Generate an array of {InvoiceItem} with the elements in provided reservation, price included.
|
||||
# Generate an array of {InvoiceItem} with the provided elements, price included.
|
||||
# @param invoice {Invoice} the parent invoice
|
||||
# @param payment_details {Hash} as generated by ShoppingCart.total
|
||||
# @param objects {Array<Reservation|Subscription|StatisticProfilePrepaidPack>}
|
||||
|
@ -9,9 +9,7 @@ class Subscriptions::Subscribe
|
||||
@operator_profile_id = operator_profile_id
|
||||
end
|
||||
|
||||
def extend_subscription(subscription, new_expiration_date, free_days)
|
||||
return subscription.free_extend(new_expiration_date, @operator_profile_id) if free_days
|
||||
|
||||
def extend_subscription(subscription, new_expiration_date)
|
||||
new_sub = Subscription.create(
|
||||
plan_id: subscription.plan_id,
|
||||
statistic_profile_id: subscription.statistic_profile_id
|
||||
|
@ -916,6 +916,7 @@ en:
|
||||
new_expiration_date: "New expiration date:"
|
||||
number_of_free_days: "Number of free days:"
|
||||
extend: "Extend"
|
||||
extend_success: "The subscription was successfully extended for free"
|
||||
# renew a subscription
|
||||
renew_subscription_modal:
|
||||
renew_subscription: "Renew the subscription"
|
||||
|
@ -416,6 +416,8 @@ en:
|
||||
group:
|
||||
#name of the user's group for administrators
|
||||
admins: 'Administrators'
|
||||
cart_items:
|
||||
free_extension: "Free extension of a subscription, until %{DATE}"
|
||||
settings:
|
||||
locked_setting: "the setting is locked."
|
||||
about_title: "\"About\" page title"
|
||||
@ -529,3 +531,4 @@ en:
|
||||
payzen_currency: "PayZen currency"
|
||||
public_agenda_module: "Public agenda module"
|
||||
renew_pack_threshold: "Threshold for packs renewal"
|
||||
pack_only_for_subscription: "Restrict packs for subscribers"
|
||||
|
Loading…
x
Reference in New Issue
Block a user