diff --git a/admin/.rakeTasks b/admin/.rakeTasks
index c6865d9a1..78308c2e6 100644
--- a/admin/.rakeTasks
+++ b/admin/.rakeTasks
@@ -4,4 +4,4 @@ You are allowed to:
1. Remove rake task
2. Add existing rake tasks
To add existing rake tasks automatically delete this file and reload the project.
--->
+-->
diff --git a/db/manifest b/db/manifest
index 68db33231..bf1ea5bc3 100755
--- a/db/manifest
+++ b/db/manifest
@@ -347,4 +347,5 @@ track_school_signups.sql
add_test_drive_types.sql
updated_subjects.sql
update_payment_history.sql
-lesson_booking_schools.sql
\ No newline at end of file
+lesson_booking_schools.sql
+lesson_booking_schools_2.sql
\ No newline at end of file
diff --git a/db/up/lesson_booking_schools_2.sql b/db/up/lesson_booking_schools_2.sql
new file mode 100644
index 000000000..7888cbeb8
--- /dev/null
+++ b/db/up/lesson_booking_schools_2.sql
@@ -0,0 +1,2 @@
+ALTER TABLE lesson_bookings ADD COLUMN same_school BOOLEAN DEFAULT FALSE NOT NULL;
+ALTER TABLE schools ADD COLUMN affiliate_partner_id INTEGER REFERENCES affiliate_partners(id);
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/app/uploaders/artifact_uploader.rb b/ruby/lib/jam_ruby/app/uploaders/artifact_uploader.rb
index 652e877c2..7b2bbab6e 100644
--- a/ruby/lib/jam_ruby/app/uploaders/artifact_uploader.rb
+++ b/ruby/lib/jam_ruby/app/uploaders/artifact_uploader.rb
@@ -52,7 +52,7 @@ class ArtifactUploader < CarrierWave::Uploader::Base
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
- %w(exe msi dmg)
+ %w(exe msi dmg zip)
end
# Override the filename of the uploaded files:
diff --git a/ruby/lib/jam_ruby/models/affiliate_partner.rb b/ruby/lib/jam_ruby/models/affiliate_partner.rb
index 0461490f7..2014c301a 100644
--- a/ruby/lib/jam_ruby/models/affiliate_partner.rb
+++ b/ruby/lib/jam_ruby/models/affiliate_partner.rb
@@ -2,6 +2,7 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
self.table_name = 'affiliate_partners'
belongs_to :partner_user, :class_name => "JamRuby::User", :foreign_key => :partner_user_id, inverse_of: :affiliate_partner
+ has_one :school, class_name: "JamRuby::School"
has_many :user_referrals, :class_name => "JamRuby::User", :foreign_key => :affiliate_referral_id
belongs_to :affiliate_legalese, :class_name => "JamRuby::AffiliateLegalese", :foreign_key => :legalese_id
has_many :sale_line_items, :class_name => 'JamRuby::SaleLineItem', foreign_key: :affiliate_referral_id
@@ -62,7 +63,7 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
# used by admin
def self.create_with_params(params={})
raise 'not supported'
- oo = self.new
+ oo = AffiliatePartner.new
oo.partner_name = params[:partner_name].try(:strip)
oo.partner_code = params[:partner_code].try(:strip).try(:downcase)
oo.partner_user = User.where(:email => params[:user_email].try(:strip)).limit(1).first
@@ -74,15 +75,25 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
# used by web
def self.create_with_web_params(user, params={})
- oo = self.new
+ oo = AffiliatePartner.new
oo.partner_name = params[:partner_name].try(:strip)
oo.partner_user = user if user # user is not required
oo.entity_type = params[:entity_type] || ENTITY_TYPES.first
- oo.signed_at = Time.now
+ signed_legalese
oo.save
oo
end
+ def self.create_from_school(school)
+ oo = AffiliatePartner.new
+ oo.partner_name = "Affiliate from School #{school.id}"
+ oo.partner_user = school.owner
+ oo.entity_type = 'Other'
+ oo.school = school
+ oo.signed_at = nil
+ oo.save
+ end
+
def self.coded_id(code=nil)
self.where(:partner_code => code).limit(1).pluck(:id).first if code.present?
end
@@ -126,7 +137,11 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
# subtract the total quantity from the freebie quantity, to see how much we should attribute to them
real_quantity = product_info[:quantity].to_i - product_info[:marked_for_redeem].to_i
- applicable_rate = shopping_cart.is_lesson? ? lesson_rate : rate
+ if shopping_cart.is_lesson?
+ applicable_rate = lesson_rate
+ else
+ applicable_rate = rate
+ end
{fee_in_cents: (product_info[:price] * 100 * real_quantity * applicable_rate.to_f).round}
else
diff --git a/ruby/lib/jam_ruby/models/lesson_booking.rb b/ruby/lib/jam_ruby/models/lesson_booking.rb
index 7e884fef4..343f41e1e 100644
--- a/ruby/lib/jam_ruby/models/lesson_booking.rb
+++ b/ruby/lib/jam_ruby/models/lesson_booking.rb
@@ -9,7 +9,7 @@ module JamRuby
@@log = Logging.logger[LessonBooking]
- attr_accessor :accepting, :countering, :canceling, :countered_slot, :countered_lesson
+ attr_accessor :accepting, :countering, :canceling, :countered_slot, :countered_lesson, :current_purchase, :current_lesson
STATUS_REQUESTED = 'requested'
STATUS_CANCELED = 'canceled'
@@ -51,6 +51,7 @@ module JamRuby
validates :recurring, inclusion: {in: [true, false]}
validates :sent_notices, inclusion: {in: [true, false]}
validates :card_presumed_ok, inclusion: {in: [true, false]}
+ validates :same_school, inclusion: {in: [true, false]}
validates :active, inclusion: {in: [true, false]}
validates :lesson_length, inclusion: {in: [30, 45, 60, 90, 120]}
validates :payment_style, inclusion: {in: PAYMENT_STYLES}
@@ -112,7 +113,20 @@ module JamRuby
# here for shopping_cart
def product_info
- {price: booked_price, real_price: booked_price, total_price: booked_price}
+ if is_test_drive?
+ real_price = 0
+ elsif is_monthly_payment?
+ raise "no purchase assigned to lesson booking for monthly payment!" if current_purchase.nil?
+ real_price = self.current_purchase.teacher_distribution.jamkazam_margin
+ else
+ if current_lesson.nil?
+ puts "OHOHOMOOMG #{self.inspect}"
+ raise "no purchase assigned to lesson booking for lesson!"
+ end
+
+ real_price = self.current_lesson.teacher_distribution.jamkazam_margin
+ end
+ {price: real_price, real_price: real_price, total_price: real_price}
end
# here for shopping_cart
def price
@@ -357,7 +371,9 @@ module JamRuby
end
def requires_teacher_distribution?(target)
- if target.is_a?(JamRuby::LessonSession)
+ if school_on_school?
+ false
+ elsif target.is_a?(JamRuby::LessonSession)
is_test_drive? || (is_normal? && !is_monthly_payment?)
elsif target.is_a?(JamRuby::LessonPackagePurchase)
is_monthly_payment?
@@ -694,6 +710,12 @@ module JamRuby
lesson_booking.school = lesson_booking.teacher.teacher.school
end
+ if user
+ lesson_booking.same_school = !!(lesson_booking.school && user.school && (lesson_booking.school.id == user.school.id))
+ else
+ lesson_booking.same_school = false
+ end
+
# two-way association slots, for before_validation loic in slot to work
lesson_booking.lesson_booking_slots = lesson_booking_slots
lesson_booking_slots.each do |slot|
@@ -718,7 +740,7 @@ module JamRuby
end
def school_on_school?
- teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id)
+ same_school
end
def self.find_bookings_needing_sessions(minimum_start_time)
@@ -760,6 +782,7 @@ module JamRuby
.joins("LEFT JOIN lesson_package_purchases ON (lesson_package_purchases.lesson_booking_id = lesson_bookings.id AND (lesson_package_purchases.year = #{current_month_first_day.year} AND lesson_package_purchases.month = #{current_month_first_day.month}))")
.where("lesson_package_purchases.id IS NULL OR (lesson_package_purchases.id IS NOT NULL AND lesson_package_purchases.post_processed = false)")
.where(payment_style: PAYMENT_STYLE_MONTHLY)
+ .where(same_school: false)
.active
.where('music_sessions.scheduled_start >= ?', current_month_first_day)
.where('music_sessions.scheduled_start <= ?', current_month_last_day).uniq
diff --git a/ruby/lib/jam_ruby/models/lesson_package_purchase.rb b/ruby/lib/jam_ruby/models/lesson_package_purchase.rb
index 66266028f..e9f49da0c 100644
--- a/ruby/lib/jam_ruby/models/lesson_package_purchase.rb
+++ b/ruby/lib/jam_ruby/models/lesson_package_purchase.rb
@@ -121,15 +121,20 @@ module JamRuby
user
end
+ # test drive purchase doesn't have a tea
def school_on_school?
- teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id)
+ if teacher
+ teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id)
+ else
+ false
+ end
end
def bill_monthly(force = false)
if school_on_school?
- success = true
+ raise "school-on-school: should not be here"
else
lesson_payment_charge.charge(force)
success = lesson_payment_charge.billed
diff --git a/ruby/lib/jam_ruby/models/lesson_session.rb b/ruby/lib/jam_ruby/models/lesson_session.rb
index c85624e77..c40d4685d 100644
--- a/ruby/lib/jam_ruby/models/lesson_session.rb
+++ b/ruby/lib/jam_ruby/models/lesson_session.rb
@@ -11,7 +11,7 @@ module JamRuby
@@log = Logging.logger[LessonSession]
delegate :sent_billing_notices, :last_billing_attempt_at, :billing_attempts, :billing_should_retry, :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, allow_nil: true
- delegate :is_test_drive?, :is_single_free?, :is_normal?, :approved_before?, :is_active?, :recurring, :is_monthly_payment?, to: :lesson_booking
+ delegate :is_test_drive?, :is_single_free?, :is_normal?, :approved_before?, :is_active?, :recurring, :is_monthly_payment?, :school_on_school?, to: :lesson_booking
delegate :pretty_scheduled_start, to: :music_session
@@ -176,13 +176,19 @@ module JamRuby
def billed
if lesson_booking.is_test_drive?
false
- else
+ elsif lesson_payment_charge
lesson_payment_charge.billed
+ else
+ false
end
end
def amount_charged
- lesson_payment_charge.amount_in_cents / 100.0
+ if lesson_payment_charge
+ lesson_payment_charge.amount_in_cents / 100.0
+ else
+ 0.0
+ end
end
def self.analysis_to_json(analysis, preserve_object = false)
@@ -398,10 +404,6 @@ module JamRuby
@parsed_analysis || analysis ? JSON.parse(analysis) : nil
end
- def school_on_school?
- teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id)
- end
-
def validate_creating
if !is_requested? && !is_approved?
self.errors.add(:status, "is not valid for a new lesson session.")
@@ -490,7 +492,13 @@ module JamRuby
query = query.where('(lesson_sessions.teacher_id = ? or music_sessions.user_id = ?)', user.id, user.id)
end
- query = query.where('lesson_bookings.card_presumed_ok = true OR (music_sessions.user_id = ?)', user.id)
+ if user.school_id
+ school_extra = "OR (lesson_bookings.school_id = '#{user.school_id}')"
+ else
+ school_extra = ''
+ end
+
+ query = query.where('lesson_bookings.card_presumed_ok = true OR (music_sessions.user_id = ?) ' + school_extra, user.id)
current_page = params[:page].nil? ? 1 : params[:page].to_i
next_page = current_page + 1
diff --git a/ruby/lib/jam_ruby/models/sale.rb b/ruby/lib/jam_ruby/models/sale.rb
index d3827ed84..42b435524 100644
--- a/ruby/lib/jam_ruby/models/sale.rb
+++ b/ruby/lib/jam_ruby/models/sale.rb
@@ -228,6 +228,11 @@ module JamRuby
if sale.valid?
+ if lesson_booking
+ lesson_booking.current_lesson = lesson_session
+ lesson_booking.current_purchase = lesson_package_purchase
+ end
+
sale_line_item = SaleLineItem.create_from_lesson_package(current_user, sale, lesson_package_type, lesson_booking)
price_info = charge_stripe_for_lesson(current_user, lesson_booking, lesson_package_type, sale_line_item, lesson_session, lesson_package_purchase, force)
@@ -251,10 +256,12 @@ module JamRuby
purchase = price_info[:purchase]
else
# should not get out of testing. This would be very rare (i.e., from a big regression). Sale is always valid at this point.
+ puts "invalid sale object"
raise "invalid sale object"
end
end
+
{sale: sale, stripe_charge: stripe_charge, purchase: purchase}
end
diff --git a/ruby/lib/jam_ruby/models/sale_line_item.rb b/ruby/lib/jam_ruby/models/sale_line_item.rb
index f5ca99b80..7dec6502d 100644
--- a/ruby/lib/jam_ruby/models/sale_line_item.rb
+++ b/ruby/lib/jam_ruby/models/sale_line_item.rb
@@ -91,6 +91,15 @@ module JamRuby
line_item
end
+ def add_referral_if_needed(user, shopping_cart, lesson_booking)
+ # if the teacher came from an affiliate, this is our chance to account for that (the student's affiliate status was accounted for in create_from_shopping_cart)
+ referral_info = user.should_attribute_sale?(shopping_cart, lesson_booking)
+
+ if referral_info
+ self.affiliate_distributions << AffiliateDistribution.create(user.affiliate_referral, referral_info[:fee_in_cents], self)
+ self.save!
+ end
+ end
# in a shopping-cart less world (ios purchase), let's reuse as much logic as possible
def self.create_from_lesson_package(current_user, sale, lesson_package_type, lesson_booking)
teacher = lesson_booking.teacher if lesson_booking
@@ -98,12 +107,20 @@ module JamRuby
line_item = create_from_shopping_cart(sale, shopping_cart, nil, nil, nil, lesson_booking)
if lesson_booking
- # if the teacher came from an affiliate, this is our chance to account for that (the student's affiliate status was accounted for in create_from_shopping_cart)
- referral_info = teacher.should_attribute_sale?(shopping_cart, lesson_booking)
- if referral_info
- line_item.affiliate_distributions << AffiliateDistribution.create(teacher.affiliate_referral, referral_info[:fee_in_cents], line_item)
- line_item.save!
+ teacher = lesson_booking.teacher
+ student = lesson_booking.student
+
+ if lesson_booking.is_test_drive?
+ # no referral for test drives
+ elsif lesson_booking.school_on_school?
+ # no referral; we don't make money on school-on-school
+ else
+ line_item.add_referral_if_needed(student, shopping_cart, lesson_booking)
+
+ if lesson_booking.school.nil?
+ line_item.add_referral_if_needed(teacher, shopping_cart, lesson_booking)
+ end
end
end
@@ -131,7 +148,7 @@ module JamRuby
# determine if we need to associate this sale with a partner
user = shopping_cart.user
- referral_info = user.should_attribute_sale?(shopping_cart, instance)
+ referral_info = user.should_attribute_sale?(shopping_cart, instance) if !instance || !instance.is_a?(LessonBooking) # all affiliate stuff is handled elsewhere
if referral_info
sale_line_item.affiliate_distributions << AffiliateDistribution.create(user.affiliate_referral, referral_info[:fee_in_cents], sale_line_item)
diff --git a/ruby/lib/jam_ruby/models/school.rb b/ruby/lib/jam_ruby/models/school.rb
index dc659b2d9..af10595c8 100644
--- a/ruby/lib/jam_ruby/models/school.rb
+++ b/ruby/lib/jam_ruby/models/school.rb
@@ -15,6 +15,7 @@ module JamRuby
attr_accessible :original_fpfile, :cropped_fpfile, :cropped_large_fpfile, :cropped_s3_path, :cropped_large_s3_path, :photo_url, :large_photo_url, :crop_selection
belongs_to :user, class_name: ::JamRuby::User, inverse_of: :owned_school
+ belongs_to :affiliate_partner, class_name: "JamRuby::AffiliatePartner"
has_many :students, class_name: ::JamRuby::User
has_many :teachers, class_name: ::JamRuby::Teacher
has_many :school_invitations, class_name: 'JamRuby::SchoolInvitation'
@@ -27,8 +28,12 @@ module JamRuby
validates :correspondence_email, email: true, allow_blank: true
validate :validate_avatar_info
+ after_create :create_affiliate
before_save :stringify_avatar_info, :if => :updating_avatar
+ def create_affiliate
+ AffiliatePartner.create_from_school(self)
+ end
def update_from_params(params)
self.name = params[:name] if params[:name].present?
self.scheduling_communication = params[:scheduling_communication] if params[:scheduling_communication].present?
diff --git a/ruby/lib/jam_ruby/models/teacher.rb b/ruby/lib/jam_ruby/models/teacher.rb
index 5135b91e4..357dc8f69 100644
--- a/ruby/lib/jam_ruby/models/teacher.rb
+++ b/ruby/lib/jam_ruby/models/teacher.rb
@@ -58,6 +58,10 @@ module JamRuby
# only show teachers with ready for session set to true
query = query.where('teachers.ready_for_session_at IS NOT NULL')
+ if params[:onlyMySchool] && params[:onlyMySchool] != 'false' && user.school_id
+ query = query.where("teachers.school_id = ?", user.school_id)
+ end
+
instruments = params[:instruments]
if instruments && !instruments.blank? && instruments.length > 0
query = query.joins("inner JOIN teachers_instruments AS tinst ON tinst.teacher_id = teachers.id")
diff --git a/ruby/lib/jam_ruby/models/teacher_distribution.rb b/ruby/lib/jam_ruby/models/teacher_distribution.rb
index 552a5959c..316061e7d 100644
--- a/ruby/lib/jam_ruby/models/teacher_distribution.rb
+++ b/ruby/lib/jam_ruby/models/teacher_distribution.rb
@@ -80,11 +80,37 @@ module JamRuby
'$%.2f' % real_distribution
end
+ def jamkazam_margin_in_cents
+ if is_test_drive?
+ 0
+ else
+ if school
+ # if school exists, use it's rate
+ rate = school.jamkazam_rate
+ else
+ # otherwise use the teacher's rate
+ rate = teacher.teacher.jamkazam_rate
+ end
+ amount_in_cents * (rate)
+ end
+ end
+
+ def jamkazam_margin
+ (jamkazam_margin_in_cents / 100).round(2)
+ end
+
def calculate_teacher_fee
if is_test_drive?
0
else
- (amount_in_cents * (teacher.teacher.jamkazam_rate + 0.03)).round
+ if school
+ # if school exists, use it's rate
+ rate = school.jamkazam_rate
+ else
+ # otherwise use the teacher's rate
+ rate = teacher.teacher.jamkazam_rate
+ end
+ (amount_in_cents * (rate + 0.03)).round
end
end
@@ -116,7 +142,7 @@ module JamRuby
if lesson_session
lesson_session.timed_description
else
- lesson_package_purchase.description
+ lesson_package_purchase.timed_description
end
end
end
diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb
index 98120a686..85c9cd061 100644
--- a/ruby/lib/jam_ruby/models/user.rb
+++ b/ruby/lib/jam_ruby/models/user.rb
@@ -70,7 +70,7 @@ module JamRuby
# bands
has_many :band_musicians, :class_name => "JamRuby::BandMusician"
has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band"
- has_one :teacher, :class_name => "JamRuby::Teacher"
+ belongs_to :teacher, :class_name => "JamRuby::Teacher", foreign_key: :teacher_id
# genres
has_many :genre_players, as: :player, class_name: "JamRuby::GenrePlayer", dependent: :destroy
@@ -1106,6 +1106,7 @@ module JamRuby
school_id = options[:school_id]
school_interest = options[:school_interest]
+ school = School.find(school_id) if school_id
user = User.new
user.validate_instruments = true
UserManager.active_record_transaction do |user_manager|
@@ -1139,10 +1140,12 @@ module JamRuby
if school_id.present?
if user.is_a_student
user.school_id = school_id
+ user.affiliate_referral = school.affiliate_partner
elsif user.is_a_teacher
school = School.find_by_id(school_id)
school_name = school ? school.name : 'a music school'
user.teacher = Teacher.build_teacher(user, validate_introduction: true, biography: "Teaches for #{school_name}", school_id: school_id)
+ user.affiliate_referral = school.affiliate_partner
end
else
if user.is_a_teacher
@@ -1869,6 +1872,7 @@ module JamRuby
return false
end
+
if affiliate_referral
referral_info = affiliate_referral.should_attribute_sale?(shopping_cart, self, instance)
else
@@ -2109,10 +2113,22 @@ module JamRuby
end
def has_rated_teacher(teacher)
+ teacher_rating(teacher).count > 0
+ end
+
+ def has_rated_student(student)
+ student_rating(student).count > 0
+ end
+
+ def teacher_rating(teacher)
if teacher.is_a?(JamRuby::User)
teacher = teacher.teacher
end
- Review.where(target_id: teacher.id).where(target_type: teacher.class.to_s).count > 0
+ Review.where(target_id: teacher.id).where(target_type: teacher.class.to_s)
+ end
+
+ def student_rating(student)
+ Review.where(target_id: student.id).where(target_type: "JamRuby::User")
end
def has_rated_student(student)
diff --git a/ruby/spec/jam_ruby/flows/monthly_recurring_lesson_spec.rb b/ruby/spec/jam_ruby/flows/monthly_recurring_lesson_spec.rb
index 8b0292922..05e707fcb 100644
--- a/ruby/spec/jam_ruby/flows/monthly_recurring_lesson_spec.rb
+++ b/ruby/spec/jam_ruby/flows/monthly_recurring_lesson_spec.rb
@@ -12,6 +12,10 @@ describe "Monthly Recurring Lesson Flow" do
let(:lesson_booking_slot_recurring2) { FactoryGirl.build(:lesson_booking_slot_recurring) }
let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] }
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
+ let(:affiliate_partner) { FactoryGirl.create(:affiliate_partner) }
+ let(:affiliate_partner2) { FactoryGirl.create(:affiliate_partner, lesson_rate: 0.30) }
+ let(:school) {FactoryGirl.create(:school)}
+
after {Timecop.return}
@@ -37,12 +41,15 @@ describe "Monthly Recurring Lesson Flow" do
########## Need validate their credit card
token = create_stripe_token
- result = user.payment_update({token: token, zip: '78759', normal: true})
+ result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
+ puts "result #{result.inspect}"
+ booking.reload
booking.card_presumed_ok.should be_true
booking.errors.any?.should be_false
- lesson.errors.any?.should be_false
booking = result[:lesson]
lesson = booking.lesson_sessions[0]
+ lesson.errors.any?.should be_false
+
booking.sent_notices.should be_true
lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
lesson.amount_charged.should be 0.0
@@ -147,6 +154,9 @@ describe "Monthly Recurring Lesson Flow" do
# artificially end the session, which is covered by other background jobs
lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save!
+
+ Timecop.travel(end_time + 1)
+
LessonSession.hourly_check
lesson_session.reload
lesson_session.analysed.should be_true
@@ -223,9 +233,259 @@ describe "Monthly Recurring Lesson Flow" do
lesson.amount_charged.should eql 0.0
lesson_session.billing_error_reason.should be_nil
- lesson_session.sent_billing_notices.should be false
+ lesson_session.sent_billing_notices.should be nil
user.reload
user.remaining_test_drives.should eql 0
- UserMailer.deliveries.length.should eql 1 # one for student
+ UserMailer.deliveries.length.should eql 0 # one for student
+ end
+
+ it "works (school on school)" do
+
+ # get user and teacher into same school
+ user.school = school
+ user.save!
+ teacher.school = school
+ teacher.save!
+
+ # if it's later in the month, we'll make 2 lesson_package_purchases (prorated one, and next month's), which can throw off some assertions later on
+ Timecop.travel(Date.new(2016, 3, 20))
+
+ # user has no test drives, no credit card on file, but attempts to book a lesson
+ booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60)
+ booking.errors.any?.should be_false
+ booking.card_presumed_ok.should be_false
+ booking.user.should eql user
+ booking.card_presumed_ok.should be_false
+ booking.sent_notices.should be_true
+ booking.booked_price.should eql 30.00
+
+
+ user.reload
+ user.stripe_customer_id.should be nil
+ user.remaining_test_drives.should eql 0
+ user.lesson_purchases.length.should eql 0
+
+ booking.lesson_sessions.length.should eql 1
+ lesson_session = booking.lesson_sessions[0]
+ lesson_session.status.should eql LessonBooking::STATUS_REQUESTED
+ booking.status.should eql LessonBooking::STATUS_REQUESTED
+
+ ######### Teacher counters with new slot
+ teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 14, update_all: true)
+ UserMailer.deliveries.clear
+ lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
+ booking.reload
+ booking.errors.any?.should be false
+ lesson_session.lesson_booking.errors.any?.should be false
+ lesson_session.lesson_booking_slots.length.should eql 1
+ lesson_session.lesson_booking_slots[0].proposer.should eql teacher_user
+ teacher_counter = lesson_session.lesson_booking_slots.order(:created_at).last
+ teacher_counter.should eql teacher_countered_slot
+ teacher_counter.proposer.should eql teacher_user
+ booking.lesson_booking_slots.length.should eql 3
+ UserMailer.deliveries.length.should eql 1
+ chat = ChatMessage.unscoped.order(:created_at).last
+ chat.channel.should eql ChatMessage::CHANNEL_LESSON
+ chat.message.should eql 'Does this work?'
+ chat.user.should eql teacher_user
+ chat.target_user.should eql user
+ notification = Notification.unscoped.order(:created_at).last
+ notification.session_id.should eql lesson_session.music_session.id
+ notification.student_directed.should eql true
+ notification.purpose.should eql 'counter'
+ notification.description.should eql NotificationTypes::LESSON_MESSAGE
+
+ ######### Student counters with new slot
+ student_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 16, update_all: true)
+ UserMailer.deliveries.clear
+ lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
+ lesson_session.errors.any?.should be false
+ lesson_session.lesson_booking.errors.any?.should be false
+ lesson_session.lesson_booking_slots.length.should eql 2
+ student_counter = booking.lesson_booking_slots.order(:created_at).last
+ student_counter.proposer.should eql user
+ booking.reload
+ booking.lesson_booking_slots.length.should eql 4
+ UserMailer.deliveries.length.should eql 1
+ chat = ChatMessage.unscoped.order(:created_at).last
+ chat.message.should eql 'Does this work better?'
+ chat.channel.should eql ChatMessage::CHANNEL_LESSON
+ chat.user.should eql user
+ chat.target_user.should eql teacher_user
+ notification = Notification.unscoped.order(:created_at).last
+ notification.session_id.should eql lesson_session.music_session.id
+ notification.student_directed.should eql false
+ notification.purpose.should eql 'counter'
+ notification.description.should eql NotificationTypes::LESSON_MESSAGE
+
+ ######## Teacher accepts slot
+ UserMailer.deliveries.clear
+ lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false})
+ UserMailer.deliveries.each do |del|
+ # puts del.inspect
+ end
+ # get acceptance emails, as well as 'your stuff is accepted'
+ UserMailer.deliveries.length.should eql 2
+ lesson_session.errors.any?.should be_false
+ lesson_session.reload
+ lesson_session.slot.should eql student_counter
+ lesson_session.status.should eql LessonSession::STATUS_APPROVED
+ booking.reload
+ booking.default_slot.should eql student_counter
+ lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
+ booking.status.should eql LessonBooking::STATUS_APPROVED
+
+ UserMailer.deliveries.length.should eql 2
+ chat = ChatMessage.unscoped.order(:created_at).last
+ chat.message.should eql 'Yeah I got this'
+ chat.purpose.should eql 'Lesson Approved'
+ chat.channel.should eql ChatMessage::CHANNEL_LESSON
+ chat.user.should eql teacher_user
+ chat.target_user.should eql user
+ notification = Notification.unscoped.order(:created_at).last
+ notification.session_id.should eql lesson_session.music_session.id
+ notification.student_directed.should eql true
+ notification.purpose.should eql 'accept'
+ notification.description.should eql NotificationTypes::LESSON_MESSAGE
+
+
+ # teacher & student get into session
+ start = lesson_session.scheduled_start
+ end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
+ uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
+ # artificially end the session, which is covered by other background jobs
+ lesson_session.music_session.session_removed_at = end_time
+ lesson_session.music_session.save!
+
+ Timecop.travel(end_time + 1)
+
+ LessonSession.hourly_check
+ lesson_session.reload
+ lesson_session.analysed.should be_true
+ analysis = JSON.parse(lesson_session.analysis)
+ analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
+ analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
+ if lesson_session.billing_error_detail
+ puts "monthly recurring lesson flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
+ end
+
+ # let user pay for it
+ LessonBooking.hourly_check
+
+ LessonPaymentCharge.count.should eql 0
+ TeacherDistribution.count.should eql 0
+
+ user.reload
+ user.lesson_purchases.length.should eql 0
+ user.sales.length.should eql 0
+
+
+ TeacherPayment.count.should eql 0
+ TeacherPayment.hourly_check
+ TeacherPayment.count.should eql 0
+
+
+ # teacher & student get into session
+ start = lesson_session.scheduled_start
+ end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
+ uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
+ # artificially end the session, which is covered by other background jobs
+ lesson_session.music_session.session_removed_at = end_time
+ lesson_session.music_session.save!
+
+
+ UserMailer.deliveries.clear
+ # background code comes around and analyses the session
+ LessonSession.hourly_check
+ lesson_session.reload
+ lesson_session.analysed.should be_true
+ analysis = JSON.parse(lesson_session.analysis)
+ analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
+ analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
+ if lesson_session.billing_error_detail
+ puts "monthly recurring lesson flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
+ end
+
+ lesson_session.amount_charged.should eql 0.0
+ lesson_session.billing_error_reason.should be_nil
+ lesson_session.sent_billing_notices.should be_nil
+ user.reload
+ user.remaining_test_drives.should eql 0
+ UserMailer.deliveries.length.should eql 0 # one for student
+
+ LessonPaymentCharge.count.should eql 0
+ TeacherDistribution.count.should eql 0
+ end
+
+
+ it "affiliate gets their cut" do
+ user.affiliate_referral = affiliate_partner
+ user.save!
+ teacher_user.affiliate_referral = affiliate_partner2
+ teacher_user.save!
+
+ lesson = monthly_lesson(user, teacher_user, true)
+
+ user.reload
+
+ user.lesson_purchases.count.should eql 1
+ lesson_package_purchase = user.lesson_purchases.first
+ teacher_distribution = lesson_package_purchase.teacher_distribution
+
+
+ user.sales.count.should eql 1
+ user.sales[0].sale_line_items[0].affiliate_distributions.count.should eql 2
+ affiliate_partner.affiliate_distributions.count.should eql 1
+ affiliate_partner2.affiliate_distributions.count.should eql 1
+ partner1_distribution = affiliate_partner.affiliate_distributions.first
+ partner2_distribution = affiliate_partner2.affiliate_distributions.first
+ partner1_distribution.sale_line_item.should eql partner2_distribution.sale_line_item
+ partner1_distribution.affiliate_referral_fee_in_cents.should eql (teacher_distribution.jamkazam_margin_in_cents * affiliate_partner.lesson_rate).round
+ partner2_distribution.affiliate_referral_fee_in_cents.should eql (teacher_distribution.jamkazam_margin_in_cents * affiliate_partner2.lesson_rate).round
+ end
+
+ it "school affiliate gets nothing when teacher school is involved" do
+ teacher.school = school
+ teacher.save!
+
+ user.affiliate_referral = affiliate_partner
+ user.save!
+
+ lesson = monthly_lesson(user, teacher_user, true)
+
+ user.sales.count.should eql 1
+ user.sales[0].sale_line_items[0].affiliate_distributions.count.should eql 1
+ user.lesson_purchases.count.should eql 1
+ lesson_package_purchase = user.lesson_purchases.first
+ teacher_distribution = lesson_package_purchase.teacher_distribution
+
+
+ affiliate_partner.affiliate_distributions.count.should eql 1
+ partner1_distribution = affiliate_partner.affiliate_distributions.first
+ partner1_distribution.affiliate_referral_fee_in_cents.should eql (teacher_distribution.jamkazam_margin_in_cents * affiliate_partner.lesson_rate).round
+ school.affiliate_partner.affiliate_distributions.count.should eql 0
+ end
+
+ it "student school affiliates gets cut when student school is involved. so does teacher's" do
+ user.affiliate_referral = school.affiliate_partner
+ user.save!
+
+ teacher_user.affiliate_referral = affiliate_partner
+ teacher_user.save!
+
+ lesson = monthly_lesson(user, teacher_user, true)
+
+ user.sales.count.should eql 1
+ user.sales[0].sale_line_items[0].affiliate_distributions.count.should eql 2
+ user.lesson_purchases.count.should eql 1
+ lesson_package_purchase = user.lesson_purchases.first
+ teacher_distribution = lesson_package_purchase.teacher_distribution
+
+
+ affiliate_partner.affiliate_distributions.count.should eql 1
+ partner1_distribution = affiliate_partner.affiliate_distributions.count.should eql 1
+ school.affiliate_partner.affiliate_distributions.count.should eql 1
+ school_partner_distribution = school.affiliate_partner.affiliate_distributions.first
+ school_partner_distribution.affiliate_referral_fee_in_cents.should eql (teacher_distribution.jamkazam_margin_in_cents * school.affiliate_partner.lesson_rate).round
end
end
diff --git a/ruby/spec/jam_ruby/flows/normal_lesson_spec.rb b/ruby/spec/jam_ruby/flows/normal_lesson_spec.rb
index 404978968..795e4c476 100644
--- a/ruby/spec/jam_ruby/flows/normal_lesson_spec.rb
+++ b/ruby/spec/jam_ruby/flows/normal_lesson_spec.rb
@@ -12,8 +12,12 @@ describe "Normal Lesson Flow" do
let(:lesson_booking_slot_recurring2) { FactoryGirl.build(:lesson_booking_slot_recurring) }
let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] }
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
+ let(:affiliate_partner) { FactoryGirl.create(:affiliate_partner) }
+ let(:affiliate_partner2) { FactoryGirl.create(:affiliate_partner, lesson_rate: 0.30) }
let(:school) {FactoryGirl.create(:school)}
+ after {Timecop.return}
+
describe "stripe mocked" do
before {
StripeMock.clear_errors
@@ -22,7 +26,6 @@ describe "Normal Lesson Flow" do
teacher.save!
}
after { StripeMock.stop }
- after {Timecop.return}
it "bill failure" do
@@ -434,22 +437,18 @@ describe "Normal Lesson Flow" do
booking.school.should be_true
booking.card_presumed_ok.should be_false
booking.user.should eql user
- user.unprocessed_normal_lesson.should eql []
- booking.sent_notices.should be_false
+ user.unprocessed_normal_lesson.should be_nil
+ booking.sent_notices.should be_true
booking.booked_price.should eql 30.00
booking.is_requested?.should be_true
- booking.sent_notices.should be_true
- lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
+ booking.lesson_sessions[0].music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
LessonPaymentCharge.count.should eql 0
user.reload
- user.stripe_customer_id.should_not be nil
+ user.stripe_customer_id.should be_nil
user.remaining_test_drives.should eql 0
user.lesson_purchases.length.should eql 0
- customer = Stripe::Customer.retrieve(user.stripe_customer_id)
- customer.email.should eql user.email
-
booking.lesson_sessions.length.should eql 1
lesson_session = booking.lesson_sessions[0]
lesson_session.status.should eql LessonBooking::STATUS_REQUESTED
@@ -550,35 +549,75 @@ describe "Normal Lesson Flow" do
if lesson_session.billing_error_detail
puts "testdrive flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
end
- lesson_session.billed.should be true
+ lesson_session.billed.should be false
user.reload
- user.lesson_purchases.length.should eql 1
- lesson_purchase = user.lesson_purchases[0]
- lesson_purchase.price.should eql 30.00
- lesson_purchase.lesson_package_type.is_normal?.should eql true
- lesson_purchase.price_in_cents.should eql 3000
- user.sales.length.should eql 1
- sale = user.sales.first
- sale.stripe_charge_id.should_not be_nil
- sale.recurly_tax_in_cents.should eql (100 * booking.booked_price.to_f * 0.0825).round.to_i
- sale.recurly_total_in_cents.should eql ((100 * booking.booked_price.to_f * 0.0825).round + 100 * booking.booked_price.to_f).to_i
- sale.recurly_subtotal_in_cents.should eql (100 * booking.booked_price).to_i
- sale.recurly_currency.should eql 'USD'
- sale.stripe_charge_id.should_not be_nil
- line_item = sale.sale_line_items[0]
- line_item.quantity.should eql 1
- line_item.product_type.should eql SaleLineItem::LESSON
- line_item.product_id.should eq LessonPackageType.single.id
- line_item.lesson_package_purchase.should eql lesson_purchase
- lesson_purchase.sale_line_item.should eql line_item
- lesson.amount_charged.should eql (sale.recurly_total_in_cents / 100.0).to_f
+ user.lesson_purchases.length.should eql 0
+ user.sales.length.should eql 0
+ lesson_session.amount_charged.should eql 0.0
lesson_session.billing_error_reason.should be_nil
- lesson_session.sent_billing_notices.should be true
+ lesson_session.sent_billing_notices.should be_nil
user.reload
user.remaining_test_drives.should eql 0
- UserMailer.deliveries.length.should eql 2 # one for student, one for teacher
+ UserMailer.deliveries.length.should eql 0 # one for student, one for teacher
LessonPaymentCharge.count.should eql 0
TeacherDistribution.count.should eql 0
end
+
+ it "affiliate gets their cut" do
+ user.affiliate_referral = affiliate_partner
+ user.save!
+ teacher_user.affiliate_referral = affiliate_partner2
+ teacher_user.save!
+
+ lesson = normal_lesson(user, teacher_user, true)
+
+ user.reload
+ user.sales.count.should eql 1
+ user.sales[0].sale_line_items[0].affiliate_distributions.count.should eql 2
+ affiliate_partner.affiliate_distributions.count.should eql 1
+ affiliate_partner2.affiliate_distributions.count.should eql 1
+ partner1_distribution = affiliate_partner.affiliate_distributions.first
+ partner2_distribution = affiliate_partner2.affiliate_distributions.first
+ partner1_distribution.sale_line_item.should eql partner2_distribution.sale_line_item
+ partner1_distribution.affiliate_referral_fee_in_cents.should eql (3000 * 0.25 * affiliate_partner.lesson_rate).round
+ partner2_distribution.affiliate_referral_fee_in_cents.should eql (3000 * 0.25 * affiliate_partner2.lesson_rate).round
+ end
+
+ it "school affiliate gets nothing when teacher school is involved" do
+ teacher.school = school
+ teacher.save!
+
+ user.affiliate_referral = affiliate_partner
+ user.save!
+
+ lesson = normal_lesson(user, teacher_user, true)
+
+ user.sales.count.should eql 1
+ user.sales[0].sale_line_items[0].affiliate_distributions.count.should eql 1
+
+ affiliate_partner.affiliate_distributions.count.should eql 1
+ partner1_distribution = affiliate_partner.affiliate_distributions.first
+ partner1_distribution.affiliate_referral_fee_in_cents.should eql (3000 * 0.25 * affiliate_partner.lesson_rate).round
+ school.affiliate_partner.affiliate_distributions.count.should eql 0
+ end
+
+ it "student school affiliates gets cut when student school is involved. so does teacher's" do
+ user.affiliate_referral = school.affiliate_partner
+ user.save!
+
+ teacher_user.affiliate_referral = affiliate_partner
+ teacher_user.save!
+
+ lesson = normal_lesson(user, teacher_user, true)
+
+ user.sales.count.should eql 1
+ user.sales[0].sale_line_items[0].affiliate_distributions.count.should eql 2
+
+ affiliate_partner.affiliate_distributions.count.should eql 1
+ partner1_distribution = affiliate_partner.affiliate_distributions.count.should eql 1
+ school.affiliate_partner.affiliate_distributions.count.should eql 1
+ school_partner_distribution = school.affiliate_partner.affiliate_distributions.first
+ school_partner_distribution.affiliate_referral_fee_in_cents.should eql (3000 * 0.25 * school.affiliate_partner.lesson_rate).round
+ end
end
diff --git a/ruby/spec/jam_ruby/flows/recurring_lesson_spec.rb b/ruby/spec/jam_ruby/flows/recurring_lesson_spec.rb
index 8eba1de70..00924d4e6 100644
--- a/ruby/spec/jam_ruby/flows/recurring_lesson_spec.rb
+++ b/ruby/spec/jam_ruby/flows/recurring_lesson_spec.rb
@@ -13,7 +13,7 @@ describe "Recurring Lesson Flow" do
let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] }
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
- before(:each) do
+ after(:each) do
Timecop.return
end
it "works" do
diff --git a/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb b/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
index 6cd7c13b0..320ce3b5a 100644
--- a/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
+++ b/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
@@ -12,12 +12,18 @@ describe "TestDrive Lesson Flow" do
let(:lesson_booking_slot_recurring2) { FactoryGirl.build(:lesson_booking_slot_recurring) }
let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] }
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
+ let(:affiliate_partner) { FactoryGirl.create(:affiliate_partner) }
+ let(:affiliate_partner2) { FactoryGirl.create(:affiliate_partner, lesson_rate: 0.30) }
+ let(:school) {FactoryGirl.create(:school)}
+
before {
- Timecop.return
teacher.stripe_account_id = stripe_account1_id
teacher.save!
}
+ after {
+ Timecop.return
+ }
it "works" do
@@ -224,5 +230,9 @@ describe "TestDrive Lesson Flow" do
teacher_payment.teacher_payment_charge.amount_in_cents.should eql 1000
teacher_payment.teacher_payment_charge.fee_in_cents.should eql 0
+ user.sales.count.should eql 1
+ sale = user.sales[0]
+ sale.sale_line_items.count.should eql 1
+ sale.sale_line_items[0].affiliate_distributions.count.should eql 0
end
end
diff --git a/ruby/spec/jam_ruby/models/lesson_booking_spec.rb b/ruby/spec/jam_ruby/models/lesson_booking_spec.rb
index 29a5f2cec..ab7ea97d3 100644
--- a/ruby/spec/jam_ruby/models/lesson_booking_spec.rb
+++ b/ruby/spec/jam_ruby/models/lesson_booking_spec.rb
@@ -291,7 +291,7 @@ describe LessonBooking do
end
describe "billable_monthlies" do
- before do
+ after do
Timecop.return
end
diff --git a/ruby/spec/jam_ruby/models/lesson_session_spec.rb b/ruby/spec/jam_ruby/models/lesson_session_spec.rb
index 938de25e7..be1a8963c 100644
--- a/ruby/spec/jam_ruby/models/lesson_session_spec.rb
+++ b/ruby/spec/jam_ruby/models/lesson_session_spec.rb
@@ -10,6 +10,7 @@ describe LessonSession do
let(:lesson_booking) {b = LessonBooking.book_normal(user, teacher, [slot1, slot2], "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60); b.card_presumed_ok = true; b.save!; b}
let(:lesson_session) {lesson_booking.lesson_sessions[0]}
+ after{Timecop.return}
describe "accept" do
it "can accept" do
@@ -18,18 +19,20 @@ describe LessonSession do
describe "upcoming_sessions_reminder" do
it "succeeds" do
+ lesson = normal_lesson(user, teacher)
UserMailer.deliveries.clear
LessonSession.upcoming_sessions_reminder
- lesson_session.touch
- lesson_session.sent_starting_notice.should be_false
- lesson_session.is_requested?.should be_true
- lesson_session.music_session.scheduled_start = 15.minutes.from_now
- lesson_session.music_session.save!
+ #UserMailer.deliveries.count.should eql 2
+ lesson.touch
+ lesson.sent_starting_notice.should be_false
+ lesson.is_approved?.should be_true
+ lesson.music_session.scheduled_start = 15.minutes.from_now
+ lesson.music_session.save!
LessonSession.upcoming_sessions_reminder
UserMailer.deliveries.count.should eql 2
UserMailer.deliveries.clear
- lesson_session.reload
- lesson_session.sent_starting_notice.should be_true
+ lesson.reload
+ lesson.sent_starting_notice.should be_true
LessonSession.upcoming_sessions_reminder
UserMailer.deliveries.count.should eql 0
end
diff --git a/ruby/spec/jam_ruby/models/music_session_spec.rb b/ruby/spec/jam_ruby/models/music_session_spec.rb
index 4013eb804..426e5cad7 100644
--- a/ruby/spec/jam_ruby/models/music_session_spec.rb
+++ b/ruby/spec/jam_ruby/models/music_session_spec.rb
@@ -527,7 +527,6 @@ describe MusicSession do
dd = Time.now - (interval.to_i + 1).days
Timecop.travel(dd)
msess1 = FactoryGirl.create(:music_session, creator: creator, scheduled_start: dd)
- Timecop.return
msess2 = FactoryGirl.create(:music_session, creator: creator)
music_sessions, user_scores = sms(searcher, default_opts)
expect(music_sessions.length).to be(1)
@@ -921,7 +920,6 @@ describe MusicSession do
dd = Time.now - (interval.to_i + 1).days
Timecop.travel(dd)
msess1 = FactoryGirl.create(:music_session)
- Timecop.return
msess2 = FactoryGirl.create(:music_session)
purging = MusicSession.purgeable_sessions
expect(purging.size).to be(1)
@@ -934,7 +932,6 @@ describe MusicSession do
dd = Time.now - (interval.to_i + 1).days
Timecop.travel(dd)
msess1 = FactoryGirl.create(:music_session, scheduled_start: Time.now)
- Timecop.return
msess2 = FactoryGirl.create(:music_session, scheduled_start: Time.now)
purging = MusicSession.purgeable_sessions
expect(purging.size).to be(1)
diff --git a/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb b/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb
index 42415be4d..edd19bbc1 100644
--- a/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb
+++ b/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb
@@ -7,6 +7,9 @@ describe MusicSessionUserHistory do
let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => music_session.creator) }
let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => some_user) }
+ after {
+ Timecop.return
+ }
describe "create" do
pending
it {user_history1.music_session_id.should == music_session.id }
@@ -31,7 +34,6 @@ describe MusicSessionUserHistory do
users = [user_history1, user_history2]
Timecop.travel(Time.now + (MusicSessionUserHistory::MIN_SESSION_DURATION_RATING * 1.5).seconds)
expect( user_history1.should_rate_session? ).to eq(true)
- Timecop.return
end
it 'should rate fails' do
diff --git a/ruby/spec/jam_ruby/models/sale_spec.rb b/ruby/spec/jam_ruby/models/sale_spec.rb
index 20291922f..7378cbb2a 100644
--- a/ruby/spec/jam_ruby/models/sale_spec.rb
+++ b/ruby/spec/jam_ruby/models/sale_spec.rb
@@ -9,7 +9,7 @@ describe Sale do
let(:jam_track3) { FactoryGirl.create(:jam_track) }
let(:gift_card) { GiftCardType.jam_track_5 }
- before(:each) {
+ after(:each) {
Timecop.return
}
def assert_free_line_item(sale_line_item, jamtrack)
@@ -698,8 +698,8 @@ describe Sale do
partner1_distribution = affiliate_partner.affiliate_distributions.first
partner2_distribution = affiliate_partner2.affiliate_distributions.first
partner1_distribution.sale_line_item.should eql partner2_distribution.sale_line_item
- partner1_distribution.affiliate_referral_fee_in_cents.should eql (3000 * affiliate_partner.lesson_rate).round
- partner2_distribution.affiliate_referral_fee_in_cents.should eql (3000 * affiliate_partner2.lesson_rate).round
+ partner1_distribution.affiliate_referral_fee_in_cents.should eql (3000 * 0.25 * affiliate_partner.lesson_rate).round
+ partner2_distribution.affiliate_referral_fee_in_cents.should eql (3000 * 0.25 * affiliate_partner2.lesson_rate).round
end
it "book recurring, monthly" do
diff --git a/ruby/spec/jam_ruby/models/teacher_payment_spec.rb b/ruby/spec/jam_ruby/models/teacher_payment_spec.rb
index bd85935dc..5debf2a3e 100644
--- a/ruby/spec/jam_ruby/models/teacher_payment_spec.rb
+++ b/ruby/spec/jam_ruby/models/teacher_payment_spec.rb
@@ -142,6 +142,7 @@ describe TeacherPayment do
end
it "charges school" do
+ teacher.touch
normal_distribution.school = school
normal_distribution.ready = true
normal_distribution.save!
diff --git a/ruby/spec/support/lesson_session.rb b/ruby/spec/support/lesson_session.rb
index 291c466a5..e21866767 100644
--- a/ruby/spec/support/lesson_session.rb
+++ b/ruby/spec/support/lesson_session.rb
@@ -1,4 +1,3 @@
-
module StripeMock
class ErrorQueue
def clear
@@ -12,13 +11,13 @@ module StripeMock
end
end
-def testdrive_lesson(user, teacher, slots = nil)
+def testdrive_lesson(user, teacher, finish = false)
- if slots.nil?
- slots = []
- slots << FactoryGirl.build(:lesson_booking_slot_single)
- slots << FactoryGirl.build(:lesson_booking_slot_single)
- end
+ #if slots.nil?
+ slots = []
+ slots << FactoryGirl.build(:lesson_booking_slot_single)
+ slots << FactoryGirl.build(:lesson_booking_slot_single)
+ #end
if user.stored_credit_card == false
user.stored_credit_card = true
@@ -27,7 +26,10 @@ def testdrive_lesson(user, teacher, slots = nil)
booking = LessonBooking.book_test_drive(user, teacher, slots, "Hey I've heard of you before.")
- #puts "BOOKING #{booking.errors.inspect}"
+ if booking.errors.any?
+ puts "BOOKING #{booking.errors.inspect}"
+ end
+
booking.errors.any?.should be_false
lesson = booking.lesson_sessions[0]
booking.card_presumed_ok.should be_true
@@ -42,21 +44,39 @@ def testdrive_lesson(user, teacher, slots = nil)
lesson.slot.should eql slots[0]
lesson.status.should eql LessonSession::STATUS_APPROVED
+ if finish
+ # teacher & student get into session
+ start = lesson.scheduled_start
+ end_time = lesson.scheduled_start + (60 * lesson.duration)
+ uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson.music_session, created_at: start, session_removed_at: end_time)
+ # artificially end the session, which is covered by other background jobs
+ lesson.music_session.session_removed_at = end_time
+ lesson.music_session.save!
+
+ Timecop.travel(end_time + 1)
+
+
+ lesson.analyse
+ lesson.session_completed
+ end
+
lesson
end
-def normal_lesson(user, teacher, slots = nil)
+def normal_lesson(user, teacher, finish = false)
- if slots.nil?
- slots = []
- slots << FactoryGirl.build(:lesson_booking_slot_single)
- slots << FactoryGirl.build(:lesson_booking_slot_single)
- end
+ #if slots.nil?
+ slots = []
+ slots << FactoryGirl.build(:lesson_booking_slot_single)
+ slots << FactoryGirl.build(:lesson_booking_slot_single)
+ #end
if user.stored_credit_card == false
- user.stored_credit_card = true
- user.save!
+ token = create_stripe_token
+ result = user.payment_update({token: token, zip: '78759', normal: true})
+ #user.stored_credit_card = true
+ #user.save!
end
booking = LessonBooking.book_normal(user, teacher, slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60)
@@ -76,21 +96,34 @@ def normal_lesson(user, teacher, slots = nil)
lesson.status.should eql LessonSession::STATUS_APPROVED
lesson.music_session.should_not be_nil
+ if finish
+ # teacher & student get into session
+ start = lesson.scheduled_start
+ end_time = lesson.scheduled_start + (60 * lesson.duration)
+ uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson.music_session, created_at: start, session_removed_at: end_time)
+ # artificially end the session, which is covered by other background jobs
+ lesson.music_session.session_removed_at = end_time
+ lesson.music_session.save!
+
+ Timecop.travel(end_time + 1)
+
+ lesson.analyse
+ lesson.session_completed
+ end
+
lesson
end
-def monthly_lesson(user, teacher, slots = nil)
+def monthly_lesson(user, teacher, finish = true)
- if slots.nil?
- slots = []
- slots << FactoryGirl.build(:lesson_booking_slot_recurring)
- slots << FactoryGirl.build(:lesson_booking_slot_recurring)
- end
+ slots = []
+ slots << FactoryGirl.build(:lesson_booking_slot_recurring)
+ slots << FactoryGirl.build(:lesson_booking_slot_recurring)
if user.stored_credit_card == false
- user.stored_credit_card = true
- user.save!
+ token = create_stripe_token
+ result = user.payment_update({token: token, zip: '78759', normal: true})
end
booking = LessonBooking.book_normal(user, teacher, slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60)
@@ -110,5 +143,29 @@ def monthly_lesson(user, teacher, slots = nil)
lesson.status.should eql LessonSession::STATUS_APPROVED
lesson.music_session.should_not be_nil
+ if finish
+ # teacher & student get into session
+ start = lesson.scheduled_start
+ end_time = lesson.scheduled_start + (60 * lesson.duration)
+ uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson.music_session, created_at: start, session_removed_at: end_time)
+ # artificially end the session, which is covered by other background jobs
+ lesson.music_session.session_removed_at = end_time
+ lesson.music_session.save!
+
+ Timecop.travel(end_time + 1)
+ lesson.analyse
+ lesson.session_completed
+
+ today = Date.today
+ if today.month == 12
+ next_month = Date.new(today.year + 1, 1, 1)
+ else
+ next_month = Date.new(today.year, today.month + 1, 1)
+ end
+
+ #Timecop.travel(next_month)
+ LessonBooking.hourly_check
+ end
+
lesson
end
\ No newline at end of file
diff --git a/web/app/assets/javascripts/homeScreen.js b/web/app/assets/javascripts/homeScreen.js
index ef94e580f..a745e1b65 100644
--- a/web/app/assets/javascripts/homeScreen.js
+++ b/web/app/assets/javascripts/homeScreen.js
@@ -92,9 +92,14 @@
$('.homecard.jamclass').on('click', function() {
- context.JK.Banner.showNotice("Coming Soon!", "JamClass is just around the corner.")
+ if (gon.jamclass_enabled) {
+ return true;
+ }
+ else {
+ context.JK.Banner.showNotice("Coming Soon!", "JamClass is just around the corner.")
+ return false;
- return false;
+ }
})
}
diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js
index 677e0c4b5..6f848d596 100644
--- a/web/app/assets/javascripts/jam_rest.js
+++ b/web/app/assets/javascripts/jam_rest.js
@@ -2480,6 +2480,16 @@
});
}
+ function createReview(options) {
+
+ return $.ajax({
+ type: "POST",
+ url: '/api/reviews',
+ dataType: "json",
+ contentType: 'application/json',
+ data: JSON.stringify(options),
+ });
+ }
function initialize() {
return self;
@@ -2703,6 +2713,7 @@
this.deleteSchoolStudent = deleteSchoolStudent;
this.listTeacherDistributions = listTeacherDistributions;
this.lessonStartTime = lessonStartTime;
+ this.createReview = createReview;
return this;
};
diff --git a/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee b/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee
index e16c0126d..e003890b6 100644
--- a/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee
@@ -96,7 +96,8 @@ UserStore = context.UserStore
userDetailDone: (response) ->
if response.id == @state.teacherId
- @setState({teacher: response, isSelf: response.id == context.JK.currentUserId})
+ school_on_school = response.teacher.school_id? && @state.user?.school_id? && response.teacher.school_id == @state.user.school_id
+ @setState({teacher: response, isSelf: response.id == context.JK.currentUserId, school_on_school: school_on_school})
else
logger.debug("BookLesson: ignoring teacher details", response.id, @state.teacherId)
@@ -226,7 +227,7 @@ UserStore = context.UserStore
booked: (response) ->
@setState({updating: false})
UserActions.refresh()
- if response.user['has_stored_credit_card?']
+ if response.user['has_stored_credit_card?'] || @state.school_on_school
context.JK.Banner.showNotice("Lesson Requested","The teacher has been notified of your lesson request, and should respond soon.
We've taken you automatically to the page for this request, and sent an email to you with a link here as well. All communication with the teacher will show up on this page and in email.")
url = "/client#/jamclass/lesson-booking/#{response.id}"
url = "/client#/jamclass"
diff --git a/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee b/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee
index bca5cb162..e4e0d5b6d 100644
--- a/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee
@@ -173,7 +173,7 @@ UserStore = context.UserStore
acceptLessonBookingFail: (response ) ->
@setState({updatingLesson: false})
- logger.debug("accept lesson booking failed", response)
+ logger.debug("accept lesson booking failed " + response.responseText)
@app.ajaxError(arguments[0], arguments[1], arguments[2])
cancelLessonBookingFail: (jqXHR) ->
@@ -699,12 +699,9 @@ UserStore = context.UserStore
renderTeacherRequested: () ->
- console.log("renderTeacherRequested")
if @isTestDrive()
- console.log("test drive")
action = `
Has requested a TestDrive {this.lessonLength()}-minute lesson, for which you will be paid $10.
`
else
- console.log("normal")
action = `
Has requested a {this.lessonDesc()} lesson, for which you will be paid {this.lessonPaymentAmt()}.