1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-07 01:54:16 +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 - Filter projects by status
- Maximum validity period for trainings authorizations - Maximum validity period for trainings authorizations
- Automatically cancel trainings with insufficient attendees - 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: 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: schedules jobs are not launched at the right time
- Fix a bug: unable to update the title of a training - Fix a bug: unable to update the title of a training

View File

@ -11,7 +11,7 @@ class API::SlotsReservationsController < API::ApiController
def update def update
authorize @slot_reservation authorize @slot_reservation
if @slot_reservation.update(slot_params) 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 render :show, status: :ok, location: @slot_reservation
else else
render json: @slot_reservation.errors, status: :unprocessable_entity render json: @slot_reservation.errors, status: :unprocessable_entity

View File

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

View File

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

View File

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

View File

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

View File

@ -170,7 +170,7 @@ class PaymentScheduleService
end end
# cancel subscription # cancel subscription
subscription = payment_schedule.payment_schedule_objects.find { |pso| pso.object_type == Subscription.name }.subscription subscription = payment_schedule.payment_schedule_objects.find { |pso| pso.object_type == Subscription.name }.subscription
subscription.expire(Time.current) subscription.expire
subscription.canceled_at subscription.canceled_at
end 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 # Extend the user's current subscription after his first training reservation if
# he subscribed to a rolling plan # he subscribed to a rolling plan
class SubscriptionExtensionAfterReservation class Subscriptions::ExtensionAfterReservation
attr_accessor :user, :reservation attr_accessor :user, :reservation
def initialize(reservation) def initialize(reservation)
@ -25,7 +25,7 @@ class SubscriptionExtensionAfterReservation
end end
def extend_subscription 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 expiration_date: reservation.slots_reservations.first.slot.start_at + user.subscribed_plan.duration
) )
end 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 end
test 'is eligible for extension because all conditions are met by default (test setup)' do 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 end
test 'not eligible if reservable is a machine' do test 'not eligible if reservable is a machine' do
@reservation_machine.save! @reservation_machine.save!
refute SubscriptionExtensionAfterReservation.new(@reservation_machine).eligible_to_extension? assert_not Subscriptions::ExtensionAfterReservation.new(@reservation_machine).eligible_to_extension?
end end
test "not eligible if user doesn't have subscription" do test "not eligible if user doesn't have subscription" do
@user.subscriptions.destroy_all @user.subscriptions.destroy_all
refute SubscriptionExtensionAfterReservation.new(@reservation_training).eligible_to_extension? assert_not Subscriptions::ExtensionAfterReservation.new(@reservation_training).eligible_to_extension?
end end
test 'not eligible if subscription is expired' do test 'not eligible if subscription is expired' do
@user.subscription.update!(expiration_date: 10.years.ago) @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 end
test "not eligible if plan attribute 'is_rolling' is false/nil" do test "not eligible if plan attribute 'is_rolling' is false/nil" do
@plan.update!(is_rolling: false) @plan.update!(is_rolling: false)
refute SubscriptionExtensionAfterReservation.new(@reservation_training).eligible_to_extension? assert_not Subscriptions::ExtensionAfterReservation.new(@reservation_training).eligible_to_extension?
end end
test 'method extend_subscription' do 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 assert_equal @reservation_training.slots_reservations.first.slot.start_at + @plan.duration, @user.subscription.expired_at
end end
end end