1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-30 19:52:20 +01:00

(bug) cannot cancel a subscription after offering free days

This commit is contained in:
Sylvain 2023-02-17 15:35:06 +01:00
parent 6c53b01741
commit 8740c4971d
11 changed files with 122 additions and 34 deletions

View File

@ -10,6 +10,7 @@
- Filter projects by status
- Maximum validity period for trainings authorizations
- Automatically cancel trainings with insufficient attendees
- Fix a bug: cannot cancel a subscription after offering free days
- Fix a bug: event image updates are not reflected unless the browser's cache is purged
- Fix a bug: schedules jobs are not launched at the right time
- Fix a bug: unable to update the title of a training

View File

@ -11,7 +11,7 @@ class API::SlotsReservationsController < API::ApiController
def update
authorize @slot_reservation
if @slot_reservation.update(slot_params)
SubscriptionExtensionAfterReservation.new(@slot_reservation.reservation).extend_subscription_if_eligible
Subscriptions::ExtensionAfterReservation.new(@slot_reservation.reservation).extend_subscription_if_eligible
render :show, status: :ok, location: @slot_reservation
else
render json: @slot_reservation.errors, status: :unprocessable_entity

View File

@ -15,7 +15,7 @@ class API::SubscriptionsController < API::ApiController
def cancel
authorize @subscription
if @subscription.expire(Time.current)
if @subscription.expire
render :show, status: :ok, location: @subscription
else
render json: { error: 'already expired' }, status: :unprocessable_entity

View File

@ -17,7 +17,7 @@ class Avoir < Invoice
end
def expire_subscription
user.subscription.expire(Time.current)
user.subscription.expire
end
private

View File

@ -121,7 +121,7 @@ class Reservation < ApplicationRecord
end
def extend_subscription
SubscriptionExtensionAfterReservation.new(self).extend_subscription_if_eligible
Subscriptions::ExtensionAfterReservation.new(self).extend_subscription_if_eligible
end
def notify_member_create_reservation

View File

@ -29,15 +29,8 @@ class Subscription < ApplicationRecord
generate_invoice(operator_profile_id).save
end
def expire(time)
if expired?
false
else
update_columns(expiration_date: time, canceled_at: time) # rubocop:disable Rails/SkipsModelValidations
notify_admin_subscription_canceled
notify_member_subscription_canceled
true
end
def expire
Subscriptions::ExpireService.call(self)
end
def expired?
@ -78,18 +71,6 @@ class Subscription < ApplicationRecord
attached_object: self
end
def notify_admin_subscription_canceled
NotificationCenter.call type: 'notify_admin_subscription_canceled',
receiver: User.admins_and_managers,
attached_object: self
end
def notify_member_subscription_canceled
NotificationCenter.call type: 'notify_member_subscription_canceled',
receiver: user,
attached_object: self
end
def notify_partner_subscribed_plan
NotificationCenter.call type: 'notify_partner_subscribed_plan',
receiver: plan.partners,

View File

@ -170,7 +170,7 @@ class PaymentScheduleService
end
# cancel subscription
subscription = payment_schedule.payment_schedule_objects.find { |pso| pso.object_type == Subscription.name }.subscription
subscription.expire(Time.current)
subscription.expire
subscription.canceled_at
end

View File

@ -0,0 +1,38 @@
# frozen_string_literal: true
# Expire the given subscription
class Subscriptions::ExpireService
class << self
# @param subscription [Subscription]
def call(subscription)
expiration = Time.current
if subscription.expired?
false
else
subscription.update_columns(expiration_date: expiration, canceled_at: expiration) # rubocop:disable Rails/SkipsModelValidations
subscription.offer_days.find_each do |od|
od.update(start_at: expiration, end_at: expiration)
end
notify_admin_subscription_canceled(subscription)
notify_member_subscription_canceled(subscription)
true
end
end
private
# @param subscription [Subscription]
def notify_admin_subscription_canceled(subscription)
NotificationCenter.call type: 'notify_admin_subscription_canceled',
receiver: User.admins_and_managers,
attached_object: subscription
end
# @param subscription [Subscription]
def notify_member_subscription_canceled(subscription)
NotificationCenter.call type: 'notify_member_subscription_canceled',
receiver: subscription.user,
attached_object: subscription
end
end
end

View File

@ -2,7 +2,7 @@
# Extend the user's current subscription after his first training reservation if
# he subscribed to a rolling plan
class SubscriptionExtensionAfterReservation
class Subscriptions::ExtensionAfterReservation
attr_accessor :user, :reservation
def initialize(reservation)
@ -25,7 +25,7 @@ class SubscriptionExtensionAfterReservation
end
def extend_subscription
user.subscription.update_columns(
user.subscription.update_columns( # rubocop:disable Rails/SkipsModelValidations
expiration_date: reservation.slots_reservations.first.slot.start_at + user.subscribed_plan.duration
)
end

View File

@ -0,0 +1,68 @@
# frozen_string_literal: true
require 'test_helper'
module Subscriptions; end
class Subscriptions::CancelTest < ActionDispatch::IntegrationTest
setup do
@admin = User.find_by(username: 'admin')
login_as(@admin, scope: :user)
end
test 'admin cancel a subscription for a user' do
subscription = Subscription.find(1)
patch "/api/subscriptions/#{subscription.id}/cancel", headers: default_headers
# Check response format & status
assert_response :success
assert_equal Mime[:json], response.content_type
# Check the subscription was canceled
subscription.reload
assert subscription.expiration_date < Time.current
assert subscription.canceled_at < Time.current
assert subscription.expired_at < Time.current
assert subscription.expired?
assert_nil subscription.user.subscribed_plan
# Notifications
notifications = Notification.where(notification_type: NotificationType.find_by(name: 'notify_admin_subscription_canceled'),
attached_object: subscription)
notified_users_ids = notifications.map(&:receiver_id)
assert_not_empty notifications
assert(User.admins.map(&:id).all? { |admin| notified_users_ids.include?(admin) })
user_notification = Notification.where(notification_type: NotificationType.find_by(name: 'notify_member_subscription_canceled'),
attached_object: subscription)
assert_equal 1, user_notification.count
end
test 'admin offer free days then cancel the subscription' do
subscription = Subscription.find(1)
new_date = 1.month.from_now.utc
post '/api/local_payment/confirm_payment',
params: {
customer_id: subscription.user.id,
items: [{ free_extension: { end_at: new_date.strftime('%Y-%m-%d %H:%M:%S.%9N Z') } }]
}.to_json, headers: default_headers
assert_response :success
patch "/api/subscriptions/#{subscription.id}/cancel", headers: default_headers
# Check response format & status
assert_response :success
assert_equal Mime[:json], response.content_type
# Check the subscription was canceled
subscription.reload
assert subscription.expiration_date < Time.current
assert subscription.canceled_at < Time.current
assert subscription.expired_at < Time.current
assert subscription.expired?
assert_nil subscription.user.subscribed_plan
end
end

View File

@ -31,31 +31,31 @@ class SubscriptionExtensionAfterReservationTest < ActiveSupport::TestCase
end
test 'is eligible for extension because all conditions are met by default (test setup)' do
assert SubscriptionExtensionAfterReservation.new(@reservation_training).eligible_to_extension?
assert Subscriptions::ExtensionAfterReservation.new(@reservation_training).eligible_to_extension?
end
test 'not eligible if reservable is a machine' do
@reservation_machine.save!
refute SubscriptionExtensionAfterReservation.new(@reservation_machine).eligible_to_extension?
assert_not Subscriptions::ExtensionAfterReservation.new(@reservation_machine).eligible_to_extension?
end
test "not eligible if user doesn't have subscription" do
@user.subscriptions.destroy_all
refute SubscriptionExtensionAfterReservation.new(@reservation_training).eligible_to_extension?
assert_not Subscriptions::ExtensionAfterReservation.new(@reservation_training).eligible_to_extension?
end
test 'not eligible if subscription is expired' do
@user.subscription.update!(expiration_date: 10.years.ago)
refute SubscriptionExtensionAfterReservation.new(@reservation_training).eligible_to_extension?
assert_not Subscriptions::ExtensionAfterReservation.new(@reservation_training).eligible_to_extension?
end
test "not eligible if plan attribute 'is_rolling' is false/nil" do
@plan.update!(is_rolling: false)
refute SubscriptionExtensionAfterReservation.new(@reservation_training).eligible_to_extension?
assert_not Subscriptions::ExtensionAfterReservation.new(@reservation_training).eligible_to_extension?
end
test 'method extend_subscription' do
SubscriptionExtensionAfterReservation.new(@reservation_training).extend_subscription
Subscriptions::ExtensionAfterReservation.new(@reservation_training).extend_subscription
assert_equal @reservation_training.slots_reservations.first.slot.start_at + @plan.duration, @user.subscription.expired_at
end
end