2019-09-10 11:46:14 +02:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# Subscription is an active or archived subscription of an User to a Plan
|
2020-03-25 10:16:47 +01:00
|
|
|
class Subscription < ApplicationRecord
|
2016-03-23 18:39:41 +01:00
|
|
|
include NotifyWith::NotificationAttachedObject
|
|
|
|
|
|
|
|
belongs_to :plan
|
2019-06-04 16:50:23 +02:00
|
|
|
belongs_to :statistic_profile
|
2016-03-23 18:39:41 +01:00
|
|
|
|
2020-11-12 16:44:55 +01:00
|
|
|
has_one :payment_schedule, as: :scheduled, dependent: :destroy
|
2016-03-23 18:39:41 +01:00
|
|
|
has_many :invoices, as: :invoiced, dependent: :destroy
|
|
|
|
has_many :offer_days, dependent: :destroy
|
|
|
|
|
|
|
|
validates_presence_of :plan_id
|
2016-04-07 12:28:25 +02:00
|
|
|
validates_with SubscriptionGroupValidator
|
2016-03-23 18:39:41 +01:00
|
|
|
|
|
|
|
# creation
|
2018-12-10 17:20:23 +01:00
|
|
|
after_save :notify_member_subscribed_plan
|
|
|
|
after_save :notify_admin_subscribed_plan
|
2016-03-23 18:39:41 +01:00
|
|
|
after_save :notify_partner_subscribed_plan, if: :of_partner_plan?
|
|
|
|
|
2019-05-29 12:01:24 +02:00
|
|
|
# @param invoice if true then only the subscription is payed, without reservation
|
|
|
|
# if false then the subscription is payed with reservation
|
2020-11-10 17:02:21 +01:00
|
|
|
def save_with_payment(operator_profile_id, invoice = true, coupon_code = nil, payment_intent_id = nil, schedule = nil)
|
2018-12-06 18:26:01 +01:00
|
|
|
return false unless valid?
|
|
|
|
|
2018-12-11 15:07:21 +01:00
|
|
|
set_expiration_date
|
2018-12-06 18:26:01 +01:00
|
|
|
return false unless save
|
|
|
|
|
2018-12-10 17:20:23 +01:00
|
|
|
UsersCredits::Manager.new(user: user).reset_credits
|
2018-12-06 18:26:01 +01:00
|
|
|
if invoice
|
|
|
|
@wallet_amount_debit = get_wallet_amount_debit
|
|
|
|
|
|
|
|
# debit wallet
|
|
|
|
wallet_transaction = debit_user_wallet
|
|
|
|
|
2020-11-10 17:02:21 +01:00
|
|
|
payment = if schedule
|
|
|
|
generate_schedule(operator_profile_id, coupon_code, payment_intent_id)
|
|
|
|
else
|
|
|
|
generate_invoice(operator_profile_id, coupon_code, payment_intent_id)
|
|
|
|
end
|
|
|
|
|
2019-01-10 16:50:54 +01:00
|
|
|
if wallet_transaction
|
2020-11-10 17:02:21 +01:00
|
|
|
payment.wallet_amount = @wallet_amount_debit
|
|
|
|
payment.wallet_transaction_id = wallet_transaction.id
|
2018-12-06 18:26:01 +01:00
|
|
|
end
|
2020-11-10 17:02:21 +01:00
|
|
|
payment.save
|
2018-12-06 18:26:01 +01:00
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2020-11-10 17:02:21 +01:00
|
|
|
def generate_schedule(operator_profile_id, coupon_code = nil, payment_intent_id = nil)
|
|
|
|
operator = InvoicingProfile.find(operator_profile_id)&.user
|
2020-11-16 16:37:40 +01:00
|
|
|
method = operator&.admin? || (operator&.manager? && operator != user) ? nil : 'stripe' # FIXME, paiement à l'accueil
|
2020-11-10 17:02:21 +01:00
|
|
|
coupon = Coupon.find_by(code: coupon_code) unless coupon_code.nil?
|
|
|
|
|
2020-11-16 16:37:40 +01:00
|
|
|
schedule = PaymentScheduleService.new.create(self, plan.amount, coupon: coupon, operator: operator, payment_method: method, user: user)
|
2020-11-10 17:02:21 +01:00
|
|
|
end
|
|
|
|
|
2019-09-10 17:57:46 +02:00
|
|
|
def generate_invoice(operator_profile_id, coupon_code = nil, payment_intent_id = nil)
|
2016-08-11 18:17:28 +02:00
|
|
|
coupon_id = nil
|
|
|
|
total = plan.amount
|
2020-05-04 11:36:06 +02:00
|
|
|
operator = InvoicingProfile.find(operator_profile_id)&.user
|
|
|
|
method = operator&.admin? || (operator&.manager? && operator != user) ? nil : 'stripe'
|
2016-08-11 18:17:28 +02:00
|
|
|
|
|
|
|
unless coupon_code.nil?
|
2016-11-24 17:57:48 +01:00
|
|
|
@coupon = Coupon.find_by(code: coupon_code)
|
|
|
|
|
|
|
|
unless @coupon.nil?
|
2016-11-29 14:57:43 +01:00
|
|
|
total = CouponService.new.apply(plan.amount, @coupon, user.id)
|
2016-11-24 17:57:48 +01:00
|
|
|
coupon_id = @coupon.id
|
2016-09-22 18:03:33 +02:00
|
|
|
end
|
2016-08-11 18:17:28 +02:00
|
|
|
end
|
|
|
|
|
2019-06-11 16:56:11 +02:00
|
|
|
invoice = Invoice.new(
|
|
|
|
invoiced_id: id,
|
|
|
|
invoiced_type: 'Subscription',
|
|
|
|
invoicing_profile: user.invoicing_profile,
|
|
|
|
statistic_profile: user.statistic_profile,
|
|
|
|
total: total,
|
|
|
|
coupon_id: coupon_id,
|
2019-09-10 17:57:46 +02:00
|
|
|
operator_profile_id: operator_profile_id,
|
2019-09-17 15:13:20 +02:00
|
|
|
stp_payment_intent_id: payment_intent_id,
|
|
|
|
payment_method: method
|
2019-06-11 16:56:11 +02:00
|
|
|
)
|
|
|
|
invoice.invoice_items.push InvoiceItem.new(
|
|
|
|
amount: plan.amount,
|
|
|
|
description: plan.name,
|
|
|
|
subscription_id: id
|
|
|
|
)
|
2016-03-23 18:39:41 +01:00
|
|
|
invoice
|
|
|
|
end
|
|
|
|
|
2019-09-09 17:37:54 +02:00
|
|
|
def generate_and_save_invoice(operator_profile_id)
|
|
|
|
generate_invoice(operator_profile_id).save
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def cancel
|
2019-12-02 11:57:25 +01:00
|
|
|
update_columns(canceled_at: DateTime.current)
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def expire(time)
|
2018-12-06 18:26:01 +01:00
|
|
|
if !expired?
|
2018-12-10 17:20:23 +01:00
|
|
|
update_columns(expiration_date: time, canceled_at: time)
|
2016-03-23 18:39:41 +01:00
|
|
|
notify_admin_subscription_canceled
|
|
|
|
notify_member_subscription_canceled
|
|
|
|
true
|
|
|
|
else
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-06 18:26:01 +01:00
|
|
|
def expired?
|
2019-12-02 11:57:25 +01:00
|
|
|
expired_at <= DateTime.current
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
|
2018-12-10 13:24:00 +01:00
|
|
|
def expired_at
|
|
|
|
last_offered = offer_days.order(:end_at).last
|
|
|
|
return last_offered.end_at if last_offered
|
|
|
|
|
|
|
|
expiration_date
|
|
|
|
end
|
|
|
|
|
2019-06-12 12:22:38 +02:00
|
|
|
def free_extend(expiration, operator_profile_id)
|
2018-12-10 13:24:00 +01:00
|
|
|
return false if expiration <= expired_at
|
|
|
|
|
|
|
|
od = offer_days.create(start_at: expired_at, end_at: expiration)
|
2019-06-11 16:56:11 +02:00
|
|
|
invoice = Invoice.new(
|
|
|
|
invoiced_id: od.id,
|
|
|
|
invoiced_type: 'OfferDay',
|
|
|
|
invoicing_profile: user.invoicing_profile,
|
|
|
|
statistic_profile: user.statistic_profile,
|
2019-06-12 12:22:38 +02:00
|
|
|
operator_profile_id: operator_profile_id,
|
2019-06-11 16:56:11 +02:00
|
|
|
total: 0
|
|
|
|
)
|
2018-12-10 13:24:00 +01:00
|
|
|
invoice.invoice_items.push InvoiceItem.new(amount: 0, description: plan.name, subscription_id: id)
|
|
|
|
invoice.save
|
2016-03-23 18:39:41 +01:00
|
|
|
|
|
|
|
if save
|
2018-12-10 13:24:00 +01:00
|
|
|
notify_subscription_extended(true)
|
2016-03-23 18:39:41 +01:00
|
|
|
return true
|
|
|
|
end
|
2018-12-06 18:26:01 +01:00
|
|
|
false
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
|
2019-06-04 16:50:23 +02:00
|
|
|
def user
|
|
|
|
statistic_profile.user
|
|
|
|
end
|
|
|
|
|
2016-03-23 18:39:41 +01:00
|
|
|
private
|
2018-12-06 18:26:01 +01:00
|
|
|
|
2016-03-23 18:39:41 +01:00
|
|
|
def notify_member_subscribed_plan
|
|
|
|
NotificationCenter.call type: 'notify_member_subscribed_plan',
|
|
|
|
receiver: user,
|
|
|
|
attached_object: self
|
|
|
|
end
|
|
|
|
|
|
|
|
def notify_admin_subscribed_plan
|
|
|
|
NotificationCenter.call type: 'notify_admin_subscribed_plan',
|
|
|
|
receiver: User.admins,
|
|
|
|
attached_object: self
|
|
|
|
end
|
|
|
|
|
|
|
|
def notify_admin_subscription_canceled
|
|
|
|
NotificationCenter.call type: 'notify_admin_subscription_canceled',
|
2020-04-29 15:34:30 +02:00
|
|
|
receiver: User.admins_and_managers,
|
2016-03-23 18:39:41 +01:00
|
|
|
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,
|
|
|
|
attached_object: self
|
|
|
|
end
|
|
|
|
|
|
|
|
def notify_subscription_extended(free_days)
|
|
|
|
meta_data = {}
|
2019-02-26 10:45:12 +01:00
|
|
|
meta_data[:free_days] = true if free_days
|
|
|
|
NotificationCenter.call type: :notify_member_subscription_extended,
|
|
|
|
receiver: user,
|
|
|
|
attached_object: self,
|
|
|
|
meta_data: meta_data
|
2016-03-23 18:39:41 +01:00
|
|
|
|
2019-02-26 10:45:12 +01:00
|
|
|
NotificationCenter.call type: :notify_admin_subscription_extended,
|
2020-04-29 15:34:30 +02:00
|
|
|
receiver: User.admins_and_managers,
|
2019-02-26 10:45:12 +01:00
|
|
|
attached_object: self,
|
|
|
|
meta_data: meta_data
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
|
2018-12-11 15:07:21 +01:00
|
|
|
def set_expiration_date
|
2020-03-04 11:43:12 +01:00
|
|
|
start_at = DateTime.current.in_time_zone
|
2018-12-11 15:07:21 +01:00
|
|
|
self.expiration_date = start_at + plan.duration
|
|
|
|
end
|
|
|
|
|
2016-03-23 18:39:41 +01:00
|
|
|
def of_partner_plan?
|
|
|
|
plan.is_a?(PartnerPlan)
|
|
|
|
end
|
|
|
|
|
2016-07-08 17:24:51 +02:00
|
|
|
def get_wallet_amount_debit
|
|
|
|
total = plan.amount
|
2018-12-06 18:26:01 +01:00
|
|
|
total = CouponService.new.apply(total, @coupon, user.id) if @coupon
|
2016-07-08 17:24:51 +02:00
|
|
|
wallet_amount = (user.wallet.amount * 100).to_i
|
2018-12-06 18:26:01 +01:00
|
|
|
wallet_amount >= total ? total : wallet_amount
|
2016-07-08 17:24:51 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def debit_user_wallet
|
2019-09-10 17:57:46 +02:00
|
|
|
return if !@wallet_amount_debit.present? || @wallet_amount_debit.zero?
|
2018-12-06 18:26:01 +01:00
|
|
|
|
|
|
|
amount = @wallet_amount_debit / 100.0
|
|
|
|
WalletService.new(user: user, wallet: user.wallet).debit(amount, self)
|
2016-07-08 17:24:51 +02:00
|
|
|
end
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|