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
|
def update
|
||||||
authorize @subscription
|
authorize @subscription
|
||||||
|
|
||||||
free_days = params[:subscription][:free] || false
|
|
||||||
|
|
||||||
res = Subscriptions::Subscribe.new(current_user.invoicing_profile.id)
|
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)
|
if res.is_a?(Subscription)
|
||||||
@subscription = res
|
@subscription = res
|
||||||
render status: :created
|
render status: :created
|
||||||
|
@ -8,7 +8,8 @@ import FormatLib from '../../lib/format';
|
|||||||
import { Loader } from '../base/loader';
|
import { Loader } from '../base/loader';
|
||||||
import { react2angular } from 'react2angular';
|
import { react2angular } from 'react2angular';
|
||||||
import { IApplication } from '../../models/application';
|
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;
|
declare const Application: IApplication;
|
||||||
|
|
||||||
@ -16,14 +17,15 @@ interface FreeExtendModalProps {
|
|||||||
isOpen: boolean,
|
isOpen: boolean,
|
||||||
toggleModal: () => void,
|
toggleModal: () => void,
|
||||||
subscription: Subscription,
|
subscription: Subscription,
|
||||||
onSuccess: (subscription: Subscription) => void,
|
customerId: number,
|
||||||
|
onSuccess: (message: string, newExpirationDate: Date) => void,
|
||||||
onError: (message: string) => void,
|
onError: (message: string) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modal dialog shown to extend the current subscription of a customer, for free
|
* 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 { t } = useTranslation('admin');
|
||||||
|
|
||||||
const [expirationDate, setExpirationDate] = useState<Date>(new Date(subscription.expired_at));
|
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
|
* Callback triggered when the user validates the free extent of the subscription
|
||||||
*/
|
*/
|
||||||
const handleConfirmExtend = (): void => {
|
const handleConfirmExtend = (): void => {
|
||||||
SubscriptionAPI.update({
|
LocalPaymentAPI.confirmPayment({
|
||||||
id: subscription.id,
|
customer_id: customerId,
|
||||||
expired_at: expirationDate,
|
payment_method: PaymentMethod.Other,
|
||||||
free: true
|
items: [
|
||||||
}).then(res => onSuccess(res))
|
{
|
||||||
.catch(err => onError(err));
|
free_extension: {
|
||||||
|
end_at: expirationDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).then(() => {
|
||||||
|
onSuccess(t('app.admin.free_extend_modal.extend_success'), expirationDate);
|
||||||
|
toggleModal();
|
||||||
|
}).catch(err => onError(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 (
|
return (
|
||||||
<Loader>
|
<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>
|
</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
|
* Opens/closes the modal dialog to freely extend the subscription
|
||||||
*/
|
*/
|
||||||
$scope.toggleFreeExtendModal = () => {
|
$scope.toggleFreeExtendModal = () => {
|
||||||
$scope.isOpenFreeExtendModal = !$scope.isOpenFreeExtendModal;
|
setTimeout(() => {
|
||||||
|
$scope.isOpenFreeExtendModal = !$scope.isOpenFreeExtendModal;
|
||||||
|
$scope.$apply();
|
||||||
|
}, 50);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback triggered if the subscription was successfully extended
|
* Callback triggered if the subscription was successfully extended
|
||||||
*/
|
*/
|
||||||
$scope.onExtendSuccess = (subscription) => {
|
$scope.onExtendSuccess = (message, newExpirationDate) => {
|
||||||
$scope.subscription = subscription;
|
growl.success(message);
|
||||||
|
$scope.subscription.expired_at = newExpirationDate;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,7 +21,10 @@ export enum PaymentMethod {
|
|||||||
Other = ''
|
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 {
|
export interface ShoppingCart {
|
||||||
customer_id: number,
|
customer_id: number,
|
||||||
|
@ -82,6 +82,7 @@
|
|||||||
<free-extend-modal is-open="isOpenFreeExtendModal"
|
<free-extend-modal is-open="isOpenFreeExtendModal"
|
||||||
toggle-modal="toggleFreeExtendModal"
|
toggle-modal="toggleFreeExtendModal"
|
||||||
subscription="subscription"
|
subscription="subscription"
|
||||||
|
customer-id="user.id"
|
||||||
on-error="onError"
|
on-error="onError"
|
||||||
on-success="onExtendSuccess">
|
on-success="onExtendSuccess">
|
||||||
</free-extend-modal>
|
</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
|
has_many :invoice_items, as: :object, dependent: :destroy
|
||||||
belongs_to :subscription
|
belongs_to :subscription
|
||||||
|
|
||||||
|
after_create :notify_subscription_extended
|
||||||
|
|
||||||
# buying invoice
|
# buying invoice
|
||||||
def original_invoice
|
def original_invoice
|
||||||
invoice_items.select(:invoice_id)
|
invoice_items.select(:invoice_id)
|
||||||
@ -15,4 +17,20 @@ class OfferDay < ApplicationRecord
|
|||||||
.map { |id| Invoice.find_by(id: id, type: nil) }
|
.map { |id| Invoice.find_by(id: id, type: nil) }
|
||||||
.first
|
.first
|
||||||
end
|
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
|
end
|
||||||
|
@ -122,6 +122,7 @@ class Setting < ApplicationRecord
|
|||||||
renew_pack_threshold
|
renew_pack_threshold
|
||||||
pack_only_for_subscription] }
|
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
|
# 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
|
def value
|
||||||
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(1).first
|
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(1).first
|
||||||
|
@ -47,26 +47,6 @@ class Subscription < ApplicationRecord
|
|||||||
expiration_date
|
expiration_date
|
||||||
end
|
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
|
def user
|
||||||
statistic_profile.user
|
statistic_profile.user
|
||||||
end
|
end
|
||||||
@ -116,9 +96,8 @@ class Subscription < ApplicationRecord
|
|||||||
attached_object: self
|
attached_object: self
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify_subscription_extended(free_days)
|
def notify_subscription_extended
|
||||||
meta_data = {}
|
meta_data = { free_days: false }
|
||||||
meta_data[:free_days] = true if free_days
|
|
||||||
NotificationCenter.call type: :notify_member_subscription_extended,
|
NotificationCenter.call type: :notify_member_subscription_extended,
|
||||||
receiver: user,
|
receiver: user,
|
||||||
attached_object: self,
|
attached_object: self,
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
# Check the access policies for API::LocalPaymentsController
|
# Check the access policies for API::LocalPaymentsController
|
||||||
class LocalPaymentPolicy < ApplicationPolicy
|
class LocalPaymentPolicy < ApplicationPolicy
|
||||||
def confirm_payment?
|
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
|
||||||
end
|
end
|
||||||
|
@ -22,6 +22,8 @@ class CartService
|
|||||||
items.push(reservable_from_hash(item[:reservation], plan_info))
|
items.push(reservable_from_hash(item[:reservation], plan_info))
|
||||||
elsif ['prepaid_pack', :prepaid_pack].include?(item.keys.first)
|
elsif ['prepaid_pack', :prepaid_pack].include?(item.keys.first)
|
||||||
items.push(CartItem::PrepaidPack.new(PrepaidPack.find(item[:prepaid_pack][:id]), @customer))
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -70,18 +72,21 @@ class CartService
|
|||||||
|
|
||||||
def plan(cart_items)
|
def plan(cart_items)
|
||||||
new_plan_being_bought = false
|
new_plan_being_bought = false
|
||||||
|
subscription = nil
|
||||||
plan = if cart_items[:items].any? { |item| ['subscription', :subscription].include?(item.keys.first) }
|
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) }
|
index = cart_items[:items].index { |item| ['subscription', :subscription].include?(item.keys.first) }
|
||||||
if cart_items[:items][index][:subscription][:plan_id]
|
if cart_items[:items][index][:subscription][:plan_id]
|
||||||
new_plan_being_bought = true
|
new_plan_being_bought = true
|
||||||
|
subscription = cart_items[:items][index][:subscription].to_object
|
||||||
Plan.find(cart_items[:items][index][:subscription][:plan_id])
|
Plan.find(cart_items[:items][index][:subscription][:plan_id])
|
||||||
end
|
end
|
||||||
elsif @customer.subscribed_plan
|
elsif @customer.subscribed_plan
|
||||||
|
subscription = @customer.subscription unless @customer.subscription.expired_at < DateTime.current
|
||||||
@customer.subscribed_plan
|
@customer.subscribed_plan
|
||||||
else
|
else
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
{ plan: plan, new_subscription: new_plan_being_bought }
|
{ plan: plan, subscription: subscription, new_subscription: new_plan_being_bought }
|
||||||
end
|
end
|
||||||
|
|
||||||
def customer(cart_items)
|
def customer(cart_items)
|
||||||
|
@ -93,7 +93,7 @@ class InvoicesService
|
|||||||
end
|
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 invoice {Invoice} the parent invoice
|
||||||
# @param payment_details {Hash} as generated by ShoppingCart.total
|
# @param payment_details {Hash} as generated by ShoppingCart.total
|
||||||
# @param objects {Array<Reservation|Subscription|StatisticProfilePrepaidPack>}
|
# @param objects {Array<Reservation|Subscription|StatisticProfilePrepaidPack>}
|
||||||
|
@ -9,9 +9,7 @@ class Subscriptions::Subscribe
|
|||||||
@operator_profile_id = operator_profile_id
|
@operator_profile_id = operator_profile_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def extend_subscription(subscription, new_expiration_date, free_days)
|
def extend_subscription(subscription, new_expiration_date)
|
||||||
return subscription.free_extend(new_expiration_date, @operator_profile_id) if free_days
|
|
||||||
|
|
||||||
new_sub = Subscription.create(
|
new_sub = Subscription.create(
|
||||||
plan_id: subscription.plan_id,
|
plan_id: subscription.plan_id,
|
||||||
statistic_profile_id: subscription.statistic_profile_id
|
statistic_profile_id: subscription.statistic_profile_id
|
||||||
|
@ -916,6 +916,7 @@ en:
|
|||||||
new_expiration_date: "New expiration date:"
|
new_expiration_date: "New expiration date:"
|
||||||
number_of_free_days: "Number of free days:"
|
number_of_free_days: "Number of free days:"
|
||||||
extend: "Extend"
|
extend: "Extend"
|
||||||
|
extend_success: "The subscription was successfully extended for free"
|
||||||
# renew a subscription
|
# renew a subscription
|
||||||
renew_subscription_modal:
|
renew_subscription_modal:
|
||||||
renew_subscription: "Renew the subscription"
|
renew_subscription: "Renew the subscription"
|
||||||
|
@ -416,6 +416,8 @@ en:
|
|||||||
group:
|
group:
|
||||||
#name of the user's group for administrators
|
#name of the user's group for administrators
|
||||||
admins: 'Administrators'
|
admins: 'Administrators'
|
||||||
|
cart_items:
|
||||||
|
free_extension: "Free extension of a subscription, until %{DATE}"
|
||||||
settings:
|
settings:
|
||||||
locked_setting: "the setting is locked."
|
locked_setting: "the setting is locked."
|
||||||
about_title: "\"About\" page title"
|
about_title: "\"About\" page title"
|
||||||
@ -529,3 +531,4 @@ en:
|
|||||||
payzen_currency: "PayZen currency"
|
payzen_currency: "PayZen currency"
|
||||||
public_agenda_module: "Public agenda module"
|
public_agenda_module: "Public agenda module"
|
||||||
renew_pack_threshold: "Threshold for packs renewal"
|
renew_pack_threshold: "Threshold for packs renewal"
|
||||||
|
pack_only_for_subscription: "Restrict packs for subscribers"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user