2016-04-06 02:23:15 +00:00
|
|
|
# represents the purchase of a LessonPackage
|
|
|
|
|
module JamRuby
|
|
|
|
|
class LessonPackagePurchase < ActiveRecord::Base
|
|
|
|
|
|
|
|
|
|
@@log = Logging.logger[LessonPackagePurchase]
|
|
|
|
|
|
2016-09-08 10:59:58 +00:00
|
|
|
delegate :sent_billing_notices, :last_billing_attempt_at, :billing_attempts, :billing_should_retry, :billed, :billed_at,
|
|
|
|
|
:billing_error_detail, :billing_error_reason, :is_card_declined?, :is_card_expired?,
|
|
|
|
|
:last_billed_at_date, :sent_billing_notices, to: :lesson_payment_charge
|
2016-05-20 20:33:44 +00:00
|
|
|
delegate :test_drive_count, to: :lesson_package_type
|
2016-04-06 02:23:15 +00:00
|
|
|
|
|
|
|
|
# who purchased the lesson package?
|
|
|
|
|
belongs_to :user, class_name: "JamRuby::User", :foreign_key => "user_id", inverse_of: :lesson_purchases
|
|
|
|
|
belongs_to :lesson_package_type, class_name: "JamRuby::LessonPackageType"
|
|
|
|
|
belongs_to :teacher, class_name: "JamRuby::User"
|
|
|
|
|
belongs_to :lesson_booking, class_name: "JamRuby::LessonBooking"
|
|
|
|
|
belongs_to :lesson_payment_charge, class_name: "JamRuby::LessonPaymentCharge", foreign_key: :charge_id
|
2016-09-27 02:56:12 +00:00
|
|
|
belongs_to :posa_card, class_name: "JamRuby::PosaCard", foreign_key: :posa_card_id
|
2016-08-03 01:46:15 +00:00
|
|
|
has_one :lesson_session, class_name: "JamRuby::LessonSession", dependent: :destroy
|
2016-09-08 10:59:58 +00:00
|
|
|
has_many :teacher_distributions, class_name: "JamRuby::TeacherDistribution"
|
2016-04-06 02:23:15 +00:00
|
|
|
|
2016-08-03 01:46:15 +00:00
|
|
|
has_one :sale_line_item, class_name: "JamRuby::SaleLineItem", dependent: :destroy
|
2016-04-06 02:23:15 +00:00
|
|
|
|
|
|
|
|
validates :user, presence: true
|
|
|
|
|
validates :lesson_package_type, presence: true
|
|
|
|
|
validates :price, presence: true
|
|
|
|
|
validate :validate_test_drive, on: :create
|
|
|
|
|
|
|
|
|
|
after_create :add_test_drives
|
|
|
|
|
after_create :create_charge
|
|
|
|
|
|
2017-07-10 02:21:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-06 02:23:15 +00:00
|
|
|
def validate_test_drive
|
|
|
|
|
if user
|
2016-09-27 02:56:12 +00:00
|
|
|
# if this is a posa card purchase, we won't stop it from getting created
|
2017-07-10 02:21:29 +00:00
|
|
|
if posa_card
|
2016-09-27 02:56:12 +00:00
|
|
|
return
|
|
|
|
|
end
|
2017-07-10 02:21:29 +00:00
|
|
|
|
|
|
|
|
if lesson_package_type.is_test_drive?
|
|
|
|
|
if user.lesson_package_needs_purchase_id
|
|
|
|
|
# if lesson_package_needs_purchase is set, we need to let the purchase go through because the user alrady has the credits; gotta let them pay
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
if !user.can_buy_test_drive?
|
|
|
|
|
errors.add(:user, "can not buy test drive right now because you have already purchased it within the last year")
|
|
|
|
|
end
|
2016-04-06 02:23:15 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def create_charge
|
2016-09-08 10:59:58 +00:00
|
|
|
if payment_if_school_on_school? && lesson_booking && lesson_booking.is_monthly_payment?
|
2016-05-11 21:03:55 +00:00
|
|
|
self.lesson_payment_charge = LessonPaymentCharge.new
|
|
|
|
|
lesson_payment_charge.user = user
|
|
|
|
|
lesson_payment_charge.amount_in_cents = 0
|
|
|
|
|
lesson_payment_charge.fee_in_cents = 0
|
|
|
|
|
lesson_payment_charge.lesson_package_purchase = self
|
|
|
|
|
lesson_payment_charge.save!
|
|
|
|
|
end
|
2016-04-06 02:23:15 +00:00
|
|
|
end
|
|
|
|
|
|
2016-09-08 10:59:58 +00:00
|
|
|
def teacher_distribution
|
2017-01-17 18:24:49 +00:00
|
|
|
teacher_distributions.where(education: false).first
|
2016-09-08 10:59:58 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def education_distribution
|
2017-01-17 18:24:49 +00:00
|
|
|
teacher_distributions.where(education: true).first
|
2016-09-08 10:59:58 +00:00
|
|
|
end
|
|
|
|
|
|
2016-04-06 02:23:15 +00:00
|
|
|
def add_test_drives
|
2017-07-10 02:21:29 +00:00
|
|
|
if posa_card
|
2016-09-27 02:56:12 +00:00
|
|
|
#user.jamclass_credits incremented in posa_card.rb
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
2016-04-06 02:23:15 +00:00
|
|
|
if self.lesson_package_type.is_test_drive?
|
2016-05-05 02:20:38 +00:00
|
|
|
new_test_drives = user.remaining_test_drives + lesson_package_type.test_drive_count
|
2016-04-06 02:23:15 +00:00
|
|
|
User.where(id: user.id).update_all(remaining_test_drives: new_test_drives)
|
|
|
|
|
user.remaining_test_drives = new_test_drives
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-06-02 14:04:56 +00:00
|
|
|
def to_s
|
2016-06-03 04:32:09 +00:00
|
|
|
"#{name}"
|
2016-06-02 14:04:56 +00:00
|
|
|
end
|
2016-05-20 20:33:44 +00:00
|
|
|
|
2016-04-06 02:23:15 +00:00
|
|
|
def name
|
|
|
|
|
lesson_package_type.sale_display
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def amount_charged
|
|
|
|
|
lesson_payment_charge.amount_in_cents / 100.0
|
|
|
|
|
end
|
|
|
|
|
|
2017-07-10 02:21:29 +00:00
|
|
|
def beginning_of_month_at
|
|
|
|
|
Date.new(year, month, 1).to_time.utc.beginning_of_month
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def end_of_month_at
|
|
|
|
|
Date.new(year, month, 1).to_time.utc.end_of_month
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def period_over?
|
|
|
|
|
Time.now.utc > end_of_month_at
|
|
|
|
|
end
|
|
|
|
|
|
2016-09-27 02:56:12 +00:00
|
|
|
def self.create(user, lesson_booking, lesson_package_type, year = nil, month = nil, posa_card = nil)
|
2016-04-06 02:23:15 +00:00
|
|
|
purchase = LessonPackagePurchase.new
|
|
|
|
|
purchase.user = user
|
|
|
|
|
purchase.lesson_booking = lesson_booking
|
|
|
|
|
purchase.teacher = lesson_booking.teacher if lesson_booking
|
2016-09-27 02:56:12 +00:00
|
|
|
purchase.posa_card = posa_card
|
2017-07-10 02:21:29 +00:00
|
|
|
if !purchase.posa_card && lesson_booking
|
|
|
|
|
# the lesson booking has a posa card? if so, we should track that
|
|
|
|
|
purchase.posa_card = lesson_booking.posa_card
|
|
|
|
|
end
|
2016-04-06 02:23:15 +00:00
|
|
|
|
|
|
|
|
if year
|
|
|
|
|
purchase.year = year
|
|
|
|
|
purchase.month = month
|
|
|
|
|
purchase.recurring = true
|
|
|
|
|
|
2017-01-17 18:24:49 +00:00
|
|
|
|
2016-09-27 02:56:12 +00:00
|
|
|
# this is for monthly
|
2016-04-06 02:23:15 +00:00
|
|
|
if lesson_booking && lesson_booking.requires_teacher_distribution?(purchase)
|
2016-09-08 10:59:58 +00:00
|
|
|
|
2017-01-17 18:24:49 +00:00
|
|
|
if lesson_booking.posa_card.nil? && lesson_booking.payment
|
|
|
|
|
|
|
|
|
|
# if there is a payment object, it will describe how everything gets doled out
|
|
|
|
|
payment = JSON.parse(lesson_booking.payment)
|
|
|
|
|
|
|
|
|
|
teacher_split = payment["teacher"]
|
|
|
|
|
retailer_split = payment["retailer"]
|
|
|
|
|
|
|
|
|
|
retailer_rate = teacher.teacher.retailer.jamkazam_rate + APP_CONFIG.stripe[:charge_fee] # add 0.03 to account for stripe deduction
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if teacher_split && teacher_split > 0
|
|
|
|
|
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, false, (teacher_split / 100.0).round(2), retailer_rate)
|
|
|
|
|
purchase.teacher_distributions << teacher_dist
|
2017-01-18 03:22:49 +00:00
|
|
|
# price should always match the teacher_distribution, if there is one
|
2017-02-05 20:42:51 +00:00
|
|
|
purchase.price = teacher_dist.amount_in_cents / 100
|
2017-07-10 02:21:29 +00:00
|
|
|
purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
|
2017-01-17 18:24:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if retailer_split && retailer_split > 0
|
|
|
|
|
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, false, (retailer_split / 100.0).round(2), retailer_rate)
|
|
|
|
|
teacher_dist.retailer = teacher.teacher.retailer
|
|
|
|
|
teacher_dist.save
|
|
|
|
|
purchase.teacher_distributions << teacher_dist
|
2017-07-10 02:21:29 +00:00
|
|
|
purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
|
2017-01-17 18:24:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, false)
|
|
|
|
|
purchase.teacher_distributions << teacher_dist
|
2017-07-10 02:21:29 +00:00
|
|
|
purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
|
2017-01-17 18:24:49 +00:00
|
|
|
# price should always match the teacher_distribution, if there is one
|
2017-02-05 20:42:51 +00:00
|
|
|
purchase.price = teacher_dist.amount_in_cents / 100
|
2017-01-17 18:24:49 +00:00
|
|
|
|
2017-03-22 12:39:06 +00:00
|
|
|
if lesson_booking.school_on_school_payment? && lesson_booking.school.education
|
2017-01-17 18:24:49 +00:00
|
|
|
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, true)
|
|
|
|
|
purchase.teacher_distributions << teacher_dist
|
2017-07-10 02:21:29 +00:00
|
|
|
purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
|
2017-01-17 18:24:49 +00:00
|
|
|
end
|
|
|
|
|
|
2016-09-08 10:59:58 +00:00
|
|
|
end
|
2017-07-10 02:21:29 +00:00
|
|
|
|
|
|
|
|
# record expected times for times played in the month
|
|
|
|
|
purchase.expected_session_times = lesson_booking.expected_session_times
|
2016-04-06 02:23:15 +00:00
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
purchase.recurring = false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if lesson_booking
|
2017-07-10 02:21:29 +00:00
|
|
|
|
2016-05-05 02:20:38 +00:00
|
|
|
purchase.lesson_package_type = lesson_package_type ? lesson_package_type : lesson_booking.lesson_package_type
|
2016-04-26 17:36:06 +00:00
|
|
|
purchase.price = lesson_booking.booked_price if purchase.price.nil?
|
2016-04-06 02:23:15 +00:00
|
|
|
else
|
|
|
|
|
purchase.lesson_package_type = lesson_package_type
|
2016-04-26 17:36:06 +00:00
|
|
|
purchase.price = lesson_package_type.price if purchase.price.nil?
|
2016-04-06 02:23:15 +00:00
|
|
|
end
|
|
|
|
|
purchase.save
|
|
|
|
|
purchase
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def price_in_cents
|
|
|
|
|
(price * 100).to_i
|
|
|
|
|
end
|
|
|
|
|
|
2016-05-12 21:29:27 +00:00
|
|
|
def description(lesson_booking)
|
|
|
|
|
lesson_package_type.description(lesson_booking)
|
2016-04-06 02:23:15 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def stripe_description(lesson_booking)
|
|
|
|
|
description(lesson_booking)
|
|
|
|
|
end
|
|
|
|
|
|
2016-05-11 21:03:55 +00:00
|
|
|
def timed_description
|
|
|
|
|
"Lessons for the month of #{self.month_name} with #{self.lesson_booking.student.name}"
|
|
|
|
|
end
|
2016-04-06 02:23:15 +00:00
|
|
|
|
|
|
|
|
def month_name
|
|
|
|
|
if recurring
|
|
|
|
|
Date.new(year, month, 1).strftime('%B')
|
|
|
|
|
else
|
|
|
|
|
'non-monthly paid lesson'
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def student
|
|
|
|
|
user
|
|
|
|
|
end
|
|
|
|
|
|
2016-05-16 16:39:20 +00:00
|
|
|
# test drive purchase doesn't have a tea
|
2016-05-12 21:29:27 +00:00
|
|
|
def school_on_school?
|
2016-05-16 16:39:20 +00:00
|
|
|
if teacher
|
|
|
|
|
teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id)
|
|
|
|
|
else
|
|
|
|
|
false
|
|
|
|
|
end
|
2016-05-12 21:29:27 +00:00
|
|
|
end
|
2016-04-06 02:23:15 +00:00
|
|
|
|
2016-09-08 10:59:58 +00:00
|
|
|
def school_on_school_payment?
|
2017-03-22 12:39:06 +00:00
|
|
|
#!!(school_on_school? && teacher.teacher.school.education)
|
|
|
|
|
school_on_school?
|
2016-09-08 10:59:58 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def no_school_on_school_payment?
|
|
|
|
|
!!(school_on_school? && !school_on_school_payment?)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# if this is school-on-school, is payment required?
|
|
|
|
|
def payment_if_school_on_school?
|
|
|
|
|
!!(!school_on_school? || school_on_school_payment?)
|
|
|
|
|
end
|
2016-05-11 21:03:55 +00:00
|
|
|
|
2016-04-06 02:23:15 +00:00
|
|
|
def bill_monthly(force = false)
|
|
|
|
|
|
2016-09-08 11:06:28 +00:00
|
|
|
if !payment_if_school_on_school?
|
2016-09-08 10:59:58 +00:00
|
|
|
puts "SCHOOL ON SCHOOL PAYMENT OH NO"
|
2016-05-16 16:39:20 +00:00
|
|
|
raise "school-on-school: should not be here"
|
2016-05-12 21:29:27 +00:00
|
|
|
else
|
|
|
|
|
lesson_payment_charge.charge(force)
|
|
|
|
|
success = lesson_payment_charge.billed
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if success
|
2016-04-06 02:23:15 +00:00
|
|
|
self.sent_notices = true
|
|
|
|
|
self.sent_notices_at = Time.now
|
|
|
|
|
self.post_processed = true
|
|
|
|
|
self.post_processed_at = Time.now
|
|
|
|
|
self.save(:validate => false)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def is_card_declined?
|
|
|
|
|
billed == false && billing_error_reason == 'card_declined'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def is_card_expired?
|
|
|
|
|
billed == false && billing_error_reason == 'card_expired'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def last_billed_at_date
|
|
|
|
|
last_billing_attempt_at.strftime("%B %d, %Y") if last_billing_attempt_at
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def update_payment_url
|
|
|
|
|
APP_CONFIG.external_root_url + "/client#/jamclass/update-payment"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|