2016-04-06 02:23:15 +00:00
require 'spec_helper'
describe " Monthly Recurring Lesson Flow " do
let ( :user ) { FactoryGirl . create ( :user , remaining_test_drives : 0 ) }
let ( :teacher_user ) { FactoryGirl . create ( :teacher_user ) }
let ( :teacher ) { teacher_user . teacher }
let ( :lesson_booking_slot_single1 ) { FactoryGirl . build ( :lesson_booking_slot_single ) }
let ( :lesson_booking_slot_single2 ) { FactoryGirl . build ( :lesson_booking_slot_single ) }
let ( :lesson_booking_slot_recurring1 ) { FactoryGirl . build ( :lesson_booking_slot_recurring ) }
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 ] }
2016-05-16 16:39:20 +00:00
let ( :affiliate_partner ) { FactoryGirl . create ( :affiliate_partner ) }
let ( :affiliate_partner2 ) { FactoryGirl . create ( :affiliate_partner , lesson_rate : 0 . 30 ) }
let ( :school ) { FactoryGirl . create ( :school ) }
2016-04-06 02:23:15 +00:00
after { Timecop . return }
before {
teacher . stripe_account_id = stripe_account1_id
teacher . save!
}
it " works " do
# 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
2016-09-08 10:59:58 +00:00
booking . same_school . should be_false
booking . same_school_free . should be_false
2016-04-06 02:23:15 +00:00
booking . should eql user . unprocessed_normal_lesson
booking . sent_notices . should be_false
booking . booked_price . should eql 30 . 00
########## Need validate their credit card
token = create_stripe_token
2016-05-16 16:39:20 +00:00
result = user . payment_update ( { token : token , zip : '78759' , normal : true , booking_id : booking . id } )
booking . reload
2016-05-12 21:29:27 +00:00
booking . card_presumed_ok . should be_true
2016-04-06 02:23:15 +00:00
booking . errors . any? . should be_false
2016-05-12 21:29:27 +00:00
booking = result [ :lesson ]
lesson = booking . lesson_sessions [ 0 ]
2016-05-16 16:39:20 +00:00
lesson . errors . any? . should be_false
2016-04-06 02:23:15 +00:00
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
lesson . reload
user . reload
user . stripe_customer_id . should_not 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
booking . status . should eql LessonBooking :: STATUS_REQUESTED
######### Teacher counters with new slot
2016-07-17 15:16:27 +00:00
teacher_countered_slot = FactoryGirl . build ( :lesson_booking_slot_recurring , hour : 14 , update_all : true )
2016-04-06 02:23:15 +00:00
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
2016-07-17 15:16:27 +00:00
student_countered_slot = FactoryGirl . build ( :lesson_booking_slot_recurring , hour : 16 , update_all : true )
2016-04-06 02:23:15 +00:00
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
2016-05-31 13:35:04 +00:00
lesson_session . accept ( { message : 'Yeah I got this' , slot : student_counter . id , update_all : false , accepter : teacher_user } )
2016-04-06 02:23:15 +00:00
UserMailer . deliveries . each do | del |
# puts del.inspect
end
# get acceptance emails, as well as 'your stuff is accepted'
2016-04-21 14:23:29 +00:00
UserMailer . deliveries . length . should eql 2
2016-04-06 02:23:15 +00:00
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
2016-04-21 14:23:29 +00:00
UserMailer . deliveries . length . should eql 2
2016-04-06 02:23:15 +00:00
chat = ChatMessage . unscoped . order ( :created_at ) . last
2016-04-21 14:23:29 +00:00
chat . message . should eql 'Yeah I got this'
chat . purpose . should eql 'Lesson Approved'
2016-04-06 02:23:15 +00:00
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
2016-04-06 12:06:52 +00:00
# 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!
2016-05-16 16:39:20 +00:00
Timecop . travel ( end_time + 1 )
2016-04-06 12:06:52 +00:00
LessonSession . hourly_check
lesson_session . reload
lesson_session . analysed . should be_true
2016-07-17 15:16:27 +00:00
analysis = lesson_session . analysis
2016-04-06 12:06:52 +00:00
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
2016-04-06 02:23:15 +00:00
# let user pay for it
LessonBooking . hourly_check
2016-08-03 01:46:15 +00:00
booked_price = booking . booked_price
prorated = booked_price / 2
prorated_cents = ( booked_price * 100 ) . to_i
2016-04-06 02:23:15 +00:00
user . reload
user . lesson_purchases . length . should eql 1
lesson_purchase = user . lesson_purchases [ 0 ]
2016-07-17 15:16:27 +00:00
puts " LESSON_PURCHASE PRICE #{ lesson_purchase . price } "
2016-08-03 01:46:15 +00:00
lesson_purchase . price . should eql prorated
2016-04-06 02:23:15 +00:00
lesson_purchase . lesson_package_type . is_normal? . should eql true
2016-08-03 01:46:15 +00:00
lesson_purchase . price_in_cents . should eql prorated_cents
2016-04-06 02:23:15 +00:00
teacher_distribution = lesson_purchase . teacher_distribution
2016-08-03 01:46:15 +00:00
teacher_distribution . amount_in_cents . should eql prorated_cents
2016-04-06 02:23:15 +00:00
teacher_distribution . ready . should be_true
teacher_distribution . distributed . should be_false
user . sales . length . should eql 1
sale = user . sales . first
sale . stripe_charge_id . should_not be_nil
2016-08-03 01:46:15 +00:00
sale . recurly_tax_in_cents . should eql ( 100 * prorated * 0 . 0825 ) . round . to_i
sale . recurly_total_in_cents . should eql ( ( prorated * 100 * 0 . 0825 ) . round + 100 * prorated ) . to_i
sale . recurly_subtotal_in_cents . should eql prorated_cents
2016-04-06 02:23:15 +00:00
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
TeacherPayment . count . should eql 0
2016-04-21 14:23:29 +00:00
TeacherPayment . hourly_check
2016-04-06 02:23:15 +00:00
teacher_distribution . reload
teacher_distribution . distributed . should be_true
TeacherPayment . count . should eql 1
payment = TeacherPayment . first
payment . amount_in_cents . should eql 3000
payment . fee_in_cents . should eql ( 3000 * 0 . 28 ) . round
2016-05-31 13:35:04 +00:00
payment . teacher_payment_charge . amount_in_cents . should eql ( 3000 + 3000 * APP_CONFIG . stripe [ :ach_pct ] ) . round
2016-04-06 02:23:15 +00:00
payment . teacher_payment_charge . fee_in_cents . should eql ( 3000 * 0 . 28 ) . round
payment . teacher . should eql teacher_user
payment . teacher_distribution . should eql teacher_distribution
2016-09-08 10:59:58 +00:00
# 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 = 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 . 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
end
it " works (school on school education) " do
# make sure teacher can get payments
teacher . stripe_account_id = stripe_account1_id
school . user . stripe_account_id = stripe_account2_id
# get user and teacher into same school
school . education = true
school . save!
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 . should eql user . unprocessed_normal_lesson
booking . sent_notices . should be_false
booking . booked_price . should eql 30 . 00
########## Need validate their credit card
token = create_stripe_token
result = user . payment_update ( { token : token , zip : '78759' , normal : true , booking_id : booking . id } )
booking . reload
booking . card_presumed_ok . should be_true
booking . 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
lesson . reload
user . reload
user . stripe_customer_id . should_not 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
booking . status . should eql LessonBooking :: STATUS_REQUESTED
######### Teacher counters with new slot
teacher_countered_slot = FactoryGirl . build ( :lesson_booking_slot_recurring , 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_recurring , 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 , accepter : teacher_user } )
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 = 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
booked_price = booking . booked_price
prorated = booked_price / 2
prorated_cents = ( booked_price * 100 ) . to_i
user . reload
user . lesson_purchases . length . should eql 1
lesson_purchase = user . lesson_purchases [ 0 ]
puts " LESSON_PURCHASE PRICE #{ lesson_purchase . price } "
lesson_purchase . price . should eql prorated
lesson_purchase . lesson_package_type . is_normal? . should eql true
lesson_purchase . price_in_cents . should eql prorated_cents
teacher_distribution = lesson_purchase . teacher_distribution
teacher_distribution . amount_in_cents . should eql prorated_cents
teacher_distribution . ready . should be_true
teacher_distribution . distributed . should be_false
education_distribution = lesson_purchase . education_distribution
education_distribution . amount_in_cents . should eql ( prorated_cents * 0 . 0625 ) . round
education_distribution . ready . should be_true
education_distribution . distributed . should be_false
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 * prorated * 0 . 0825 ) . round . to_i
sale . recurly_total_in_cents . should eql ( ( prorated * 100 * 0 . 0825 ) . round + 100 * prorated ) . to_i
sale . recurly_subtotal_in_cents . should eql prorated_cents
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
TeacherPayment . count . should eql 0
TeacherPayment . hourly_check
teacher_distribution . reload
teacher_distribution . distributed . should be_true
TeacherPayment . count . should eql 2
payment = teacher_distribution . teacher_payment
payment . amount_in_cents . should eql 3000
payment . fee_in_cents . should eql ( 3000 * 0 . 28 ) . round
payment . teacher_payment_charge . amount_in_cents . should eql ( 3000 + 3000 * APP_CONFIG . stripe [ :ach_pct ] ) . round
payment . teacher_payment_charge . fee_in_cents . should eql ( 3000 * 0 . 28 ) . round
payment . teacher . should eql teacher_user
payment . teacher_distribution . should eql teacher_distribution
education_distribution . reload
education_distribution . distributed . should be_true
education_amt = ( 3000 * 0 . 0625 ) . round
payment = education_distribution . teacher_payment
payment . amount_in_cents . should eql education_amt
payment . fee_in_cents . should eql 0
payment . teacher_payment_charge . amount_in_cents . should eql ( education_amt + education_amt * APP_CONFIG . stripe [ :ach_pct ] ) . round
payment . teacher_payment_charge . fee_in_cents . should eql 0
payment . teacher . should eql teacher_user
payment . teacher_distribution . should eql education_distribution
2016-04-06 02:23:15 +00:00
# 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
2016-07-17 15:16:27 +00:00
analysis = lesson_session . analysis
2016-04-06 02:23:15 +00:00
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 . amount_charged . should eql 0 . 0
lesson_session . billing_error_reason . should be_nil
2016-05-16 16:39:20 +00:00
lesson_session . sent_billing_notices . should be nil
2016-04-06 02:23:15 +00:00
user . reload
user . remaining_test_drives . should eql 0
2016-05-16 16:39:20 +00:00
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
2016-07-17 15:16:27 +00:00
teacher_countered_slot = FactoryGirl . build ( :lesson_booking_slot_recurring , hour : 14 , update_all : true )
2016-05-16 16:39:20 +00:00
UserMailer . deliveries . clear
lesson_session . counter ( { proposer : teacher_user , slot : teacher_countered_slot , message : 'Does this work?' } )
2016-07-17 15:16:27 +00:00
lesson_session . errors . any? . should be_false
2016-05-16 16:39:20 +00:00
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
2016-07-17 15:16:27 +00:00
student_countered_slot = FactoryGirl . build ( :lesson_booking_slot_recurring , hour : 16 , update_all : true )
2016-05-16 16:39:20 +00:00
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
2016-05-31 13:35:04 +00:00
lesson_session . accept ( { message : 'Yeah I got this' , slot : student_counter . id , update_all : false , accepter : teacher_user } )
2016-05-16 16:39:20 +00:00
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
2016-07-17 15:16:27 +00:00
analysis = lesson_session . analysis
2016-05-16 16:39:20 +00:00
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
2016-07-17 15:16:27 +00:00
analysis = lesson_session . analysis
2016-05-16 16:39:20 +00:00
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
2016-09-08 10:59:58 +00:00
2016-05-16 16:39:20 +00:00
it " affiliate gets their cut " do
2016-05-31 13:35:04 +00:00
Timecop . travel ( 2016 , 05 , 15 )
2016-05-16 16:39:20 +00:00
user . affiliate_referral = affiliate_partner
user . save!
teacher_user . affiliate_referral = affiliate_partner2
teacher_user . save!
2016-05-21 03:13:39 +00:00
user . lesson_purchases . count . should eql 0
lesson = monthly_lesson ( user , teacher_user , { finish : true , accept : true } )
2016-05-16 16:39:20 +00:00
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
2016-05-31 13:35:04 +00:00
Timecop . travel ( 2016 , 05 , 15 )
2016-05-16 16:39:20 +00:00
teacher . school = school
teacher . save!
user . affiliate_referral = affiliate_partner
user . save!
2016-05-21 03:13:39 +00:00
lesson = monthly_lesson ( user , teacher_user , { finish : true , accept : true } )
2016-05-16 16:39:20 +00:00
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
2016-05-31 13:35:04 +00:00
# in the middle of the month so that we don't get the next month's in-advance purchase put on us
Timecop . travel ( 2016 , 05 , 15 )
2016-05-16 16:39:20 +00:00
user . affiliate_referral = school . affiliate_partner
user . save!
teacher_user . affiliate_referral = affiliate_partner
teacher_user . save!
2016-05-21 03:13:39 +00:00
lesson = monthly_lesson ( user , teacher_user , { finish : true , accept : true } )
2016-05-16 16:39:20 +00:00
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
2016-04-06 02:23:15 +00:00
end
end