2021-02-01 14:56:47 +00:00
= begin
2016-04-06 02:23:15 +00:00
require 'spec_helper'
describe " Normal 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 ) }
2016-05-12 22:41:25 +00:00
let ( :school ) { FactoryGirl . create ( :school ) }
2017-01-17 18:24:49 +00:00
let ( :retailer ) { FactoryGirl . create ( :retailer ) }
2016-04-06 02:23:15 +00:00
2016-05-16 16:39:20 +00:00
after { Timecop . return }
2016-04-06 02:23:15 +00:00
describe " stripe mocked " do
2016-05-12 21:29:27 +00:00
before {
StripeMock . clear_errors
StripeMock . start
2016-04-06 02:23:15 +00:00
teacher . stripe_account_id = stripe_account1_id
teacher . save!
}
after { StripeMock . stop }
it " bill failure " do
# user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking . book_normal ( user , teacher_user , valid_single_slots , " Hey I've heard of you before. " , false , LessonBooking :: PAYMENT_STYLE_SINGLE , 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
2016-05-12 21:29:27 +00:00
result = user . payment_update ( { token : token , zip : '78759' , normal : true , booking_id : booking . id } )
2016-04-06 02:23:15 +00:00
booking = result [ :lesson ]
lesson = booking . lesson_sessions [ 0 ]
booking . errors . any? . should be_false
lesson . errors . any? . should be_false
booking . card_presumed_ok . should be_true
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 accepts slot
UserMailer . deliveries . clear
2016-05-31 13:35:04 +00:00
lesson_session . accept ( { message : 'Yeah I got this' , slot : booking . default_slot . id , update_all : false , accepter : teacher_user } )
2016-04-06 02:23:15 +00:00
lesson_session . errors . any? . should be_false
lesson_session . reload
lesson_session . slot . should eql booking . default_slot
lesson_session . status . should eql LessonSession :: STATUS_APPROVED
booking . reload
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
# 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-12 21:29:27 +00:00
Timecop . travel ( end_time + 1 )
2016-04-06 02:23:15 +00:00
UserMailer . deliveries . clear
# background code comes around and analyses the session
StripeMock . prepare_card_error ( :card_declined )
lesson_session . lesson_payment_charge . billing_attempts . should eql 0
user . lesson_purchases . length . should eql 0
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
lesson_session . billing_attempts . should eql 1
lesson_session . billing_error_reason . should eql 'card_declined'
lesson_session . billed . should eql false
lesson_session . billing_attempts . should eql 1
user . reload
user . lesson_purchases . length . should eql 0
LessonBooking . hourly_check
lesson_session . reload
teacher_distribution = lesson_session . teacher_distribution
teacher_distribution . amount_in_cents . should eql 3000
teacher_distribution . ready . should be_false
teacher_distribution . distributed . should be_false
# let's reattempt right away; this should have no effect because we only try to bill once per 24 hours
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
lesson_session . billing_error_reason . should eql 'card_declined'
lesson_session . billed . should eql false
lesson_session . billing_attempts . should eql 1
user . reload
user . lesson_purchases . length . should eql 0
Timecop . freeze ( ( 24 + 1 ) . hours . from_now )
StripeMock . clear_errors
StripeMock . prepare_card_error ( :expired_card )
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
lesson_session . billing_attempts . should eql 2
lesson_session . billing_error_reason . should eql 'card_expired'
lesson_session . billed . should eql false
user . reload
user . lesson_purchases . length . should eql 0
Timecop . freeze ( ( 24 + 24 + 2 ) . hours . from_now )
StripeMock . clear_errors
StripeMock . prepare_card_error ( :processing_error )
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
lesson_session . billing_attempts . should eql 3
lesson_session . billing_error_reason . should eql 'processing_error'
lesson_session . billed . should eql false
user . reload
user . lesson_purchases . length . should eql 0
Timecop . freeze ( ( 24 + 24 + 24 + 3 ) . hours . from_now )
StripeMock . clear_errors
2016-07-10 01:48:22 +00:00
UserMailer . deliveries . clear
2016-04-06 02:23:15 +00:00
# finally the user will get billed!
LessonSession . hourly_check
lesson_session . reload
payment = lesson_session . lesson_payment_charge
payment . amount_in_cents . should eql 3248
payment . fee_in_cents . should eql 0
2016-05-12 21:29:27 +00:00
lesson_session . billing_attempts . should eql 4
2016-04-06 02:23:15 +00:00
lesson_session . post_processed . should be_true
2016-05-12 21:29:27 +00:00
LessonPaymentCharge . count . should eql 1
2016-04-06 02:23:15 +00:00
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
lesson_session . billing_attempts . should eql 4
if lesson_session . billing_error_detail
lesson_session . billing_error_detail
end
lesson_session . billing_error_reason . should eql 'processing_error'
lesson_session . billed . should eql true
user . reload
user . lesson_purchases . length . should eql 1
LessonBooking . hourly_check
payment . reload
payment . amount_in_cents . should eql 3248
payment . fee_in_cents . should eql 0
lesson_session . reload
teacher_distribution = lesson_session . teacher_distribution
teacher_distribution . amount_in_cents . should eql 3000
teacher_distribution . ready . should be_true
teacher_distribution . distributed . should be_false
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 . reload
lesson . amount_charged . should eql ( sale . recurly_total_in_cents / 100 . 0 ) . to_f
lesson_session . billing_error_reason . should eql 'processing_error'
lesson_session . sent_billing_notices . should be true
user . reload
user . remaining_test_drives . should eql 0
UserMailer . deliveries . length . should eql 2 # one for student, one for teacher
TeacherPayment . count . should eql 0
2016-04-06 12:06:52 +00:00
TeacherPayment . hourly_check
2016-04-06 02:23:15 +00:00
TeacherPayment . count . should eql 1
teacher_distribution . reload
teacher_distribution . distributed . should be_true
payment = TeacherPayment . first
payment . amount_in_cents . should eql 3000
payment . fee_in_cents . should eql ( 3000 * 0 . 28 ) . round
2016-10-03 02:51:34 +00:00
payment . teacher_payment_charge . amount_in_cents . should eql ( ( 3000 * 0 . 72 ) + ( 3000 * 0 . 72 ) * 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-05-20 02:42:27 +00:00
lesson_session . lesson_booking . status . should eql LessonBooking :: STATUS_COMPLETED
lesson_session . lesson_booking . success . should be_true
lesson_session . teacher . has_booked_test_drive_with_student? ( user ) . should be_false
2016-04-06 02:23:15 +00:00
end
end
it " works " do
2016-09-08 10:59:58 +00:00
# set up teacher stripe acct
teacher . stripe_account_id = stripe_account1_id
2016-04-06 02:23:15 +00:00
# user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking . book_normal ( user , teacher_user , valid_single_slots , " Hey I've heard of you before. " , false , LessonBooking :: PAYMENT_STYLE_SINGLE , 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_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-12 21:29:27 +00:00
result = user . payment_update ( { token : token , zip : '78759' , normal : true , booking_id : booking . id } )
2016-04-06 02:23:15 +00:00
booking = result [ :lesson ]
lesson = booking . lesson_sessions [ 0 ]
booking . errors . any? . should be_false
lesson . errors . any? . should be_false
booking . card_presumed_ok . should be_true
booking . sent_notices . should be_true
lesson . music_session . scheduled_start . should eql booking . default_slot . scheduled_time ( 0 )
lesson . amount_charged . should eql 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
2016-09-08 10:59:58 +00:00
teacher_user . stripe_auth . should_not be_nil
2016-04-06 02:23:15 +00:00
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_single , hour : 14 )
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
2017-07-17 00:38:40 +00:00
#lesson_session.lesson_booking_slots.length.should eql 3
2016-04-06 02:23:15 +00:00
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 )
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
2017-07-17 00:38:40 +00:00
#booking.lesson_booking_slots.length.should eql 4
2016-04-06 02:23:15 +00:00
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
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
# 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-12 21:29:27 +00:00
Timecop . travel ( end_time + 1 )
2016-04-06 02:23:15 +00:00
UserMailer . deliveries . clear
# background code comes around and analyses the session
LessonSession . hourly_check
2016-09-08 10:59:58 +00:00
2016-04-06 02:23:15 +00:00
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 " 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
2016-09-08 10:59:58 +00:00
TeacherPayment . count . should eql 0
TeacherPayment . hourly_check
TeacherPayment . count . should eql 1
teacher_distribution = lesson_session . teacher_distribution
teacher_distribution . ready . should be_true
teacher_distribution . distributed . should be_true
education_distribution = lesson_session . education_distribution
education_distribution . should be_nil
2016-04-06 02:23:15 +00:00
lesson_session . billed . should be true
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
lesson_session . billing_error_reason . should be_nil
lesson_session . sent_billing_notices . should be true
user . reload
user . remaining_test_drives . should eql 0
2016-09-08 10:59:58 +00:00
UserMailer . deliveries . length . should eql 3 # one for student, one for teacher
2016-04-06 02:23:15 +00:00
end
2016-05-12 22:41:25 +00:00
2017-03-22 12:39:06 +00:00
it " works (school on school guitarcenter) " do
2016-05-12 22:41:25 +00:00
2017-03-22 12:39:06 +00:00
gc = GuitarCenter . init
gc_owner = gc [ :user ]
gc_school = gc [ :school ]
gc_retailer = gc [ :retailer ]
# make sure teacher can get payments
teacher . stripe_account_id = stripe_account1_id
gc_school . user . stripe_account_id = stripe_account2_id
# make sure can get stripe payments
2016-05-12 22:41:25 +00:00
# get user and teacher into same school
2017-03-22 12:39:06 +00:00
gc_school . save!
user . school = gc_school
user . save!
teacher . school = gc_school
teacher . save!
# user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking . book_normal ( user , teacher_user , valid_single_slots , " Hey I've heard of you before. " , false , LessonBooking :: PAYMENT_STYLE_SINGLE , 60 )
booking . errors . any? . should be_false
booking . school . should be_true
booking . card_presumed_ok . should be_false
booking . user . should eql user
booking . same_school_free . should be_false
#user.unprocessed_normal_lesson.should be_nil
booking . sent_notices . should be_false
booking . booked_price . should eql 30 . 00
booking . is_requested? . should be_true
booking . lesson_sessions [ 0 ] . music_session . scheduled_start . should eql booking . default_slot . scheduled_time ( 0 )
LessonPaymentCharge . count . should eql 1
########## Need validate their credit card
token = create_stripe_token
result = user . payment_update ( { token : token , zip : '78759' , normal : true , booking_id : booking . id } )
booking = result [ :lesson ]
lesson = booking . lesson_sessions [ 0 ]
booking . errors . any? . should be_false
lesson . errors . any? . should be_false
booking . card_presumed_ok . should be_true
booking . sent_notices . should be_true
booking . school . is_guitar_center? . should be_true
lesson . music_session . scheduled_start . should eql booking . default_slot . scheduled_time ( 0 )
lesson . amount_charged . should eql 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_single , hour : 14 )
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
2017-07-17 00:38:40 +00:00
#booking.lesson_sessions.lesson_booking_slots.length.should eql 3
2017-03-22 12:39:06 +00:00
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 )
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
2017-07-17 00:38:40 +00:00
#booking.lesson_booking_slots.length.should eql 4
2017-03-22 12:39:06 +00:00
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 } )
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 )
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
lesson_session . billed . should be_true
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 . billing_attempts . should eql 1
user . reload
user . lesson_purchases . length . should eql 1
LessonBooking . hourly_check
lesson_session . reload
teacher_distribution = lesson_session . teacher_distribution
teacher_distribution . amount_in_cents . should eql 3000
teacher_distribution . ready . should be_true
teacher_distribution . distributed . should be_false
lesson_session . teacher_distributions . count . should eql 1
education_distribution = lesson_session . education_distribution
education_distribution . should be_nil
lesson_session . billed . should be true
user . reload
user . lesson_purchases . length . should eql 1
user . sales . length . should eql 1
lesson_session . amount_charged . should eql 32 . 48
lesson_session . billing_error_reason . should be_nil
lesson_session . sent_billing_notices . should be_true
user . reload
user . remaining_test_drives . should eql 0
UserMailer . deliveries . length . should eql 2 # one for student, one for teacher
TeacherPayment . count . should eql 0
TeacherPayment . hourly_check
TeacherPayment . count . should eql 1
LessonPaymentCharge . count . should eql 1
TeacherDistribution . count . should eql 1
teacher_distribution . reload
teacher_distribution . distributed . should be_true
payment = teacher_distribution . teacher_payment
payment . amount_in_cents . should eql 3000
payment . fee_in_cents . should eql ( 3000 * ( gc_school . base_rate + 0 . 03 ) ) . round
payment . teacher_payment_charge . amount_in_cents . should eql ( ( teacher_distribution . amount_in_cents - teacher_distribution . teacher_fee_in_cents ) + ( teacher_distribution . amount_in_cents - teacher_distribution . teacher_fee_in_cents ) * APP_CONFIG . stripe [ :ach_pct ] ) . round
payment . teacher_payment_charge . fee_in_cents . should eql payment . teacher_payment_charge . fee_in_cents
payment . teacher . should eql teacher_user
payment . teacher_distribution . should eql teacher_distribution
lesson_session . lesson_booking . status . should eql LessonBooking :: STATUS_COMPLETED
lesson_session . lesson_booking . success . should be_true
end
it " works (school on school only) " do
# make sure teacher can get payments
teacher . stripe_account_id = stripe_account1_id
school . user . stripe_account_id = stripe_account2_id
# make sure can get stripe payments
# get user and teacher into same school
school . save!
2016-05-12 22:41:25 +00:00
user . school = school
user . save!
teacher . school = school
teacher . save!
# user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking . book_normal ( user , teacher_user , valid_single_slots , " Hey I've heard of you before. " , false , LessonBooking :: PAYMENT_STYLE_SINGLE , 60 )
booking . errors . any? . should be_false
booking . school . should be_true
booking . card_presumed_ok . should be_false
booking . user . should eql user
2017-03-22 12:39:06 +00:00
booking . same_school_free . should be_false
#user.unprocessed_normal_lesson.should be_nil
booking . sent_notices . should be_false
2016-05-12 22:41:25 +00:00
booking . booked_price . should eql 30 . 00
booking . is_requested? . should be_true
2016-05-16 16:39:20 +00:00
booking . lesson_sessions [ 0 ] . music_session . scheduled_start . should eql booking . default_slot . scheduled_time ( 0 )
2017-03-22 12:39:06 +00:00
LessonPaymentCharge . count . should eql 1
########## Need validate their credit card
token = create_stripe_token
result = user . payment_update ( { token : token , zip : '78759' , normal : true , booking_id : booking . id } )
booking = result [ :lesson ]
lesson = booking . lesson_sessions [ 0 ]
booking . errors . any? . should be_false
lesson . errors . any? . should be_false
booking . card_presumed_ok . should be_true
booking . sent_notices . should be_true
lesson . music_session . scheduled_start . should eql booking . default_slot . scheduled_time ( 0 )
lesson . amount_charged . should eql 0 . 0
lesson . reload
2016-05-12 22:41:25 +00:00
user . reload
2017-03-22 12:39:06 +00:00
user . stripe_customer_id . should_not be nil
2016-05-12 22:41:25 +00:00
user . remaining_test_drives . should eql 0
user . lesson_purchases . length . should eql 0
2017-03-22 12:39:06 +00:00
customer = Stripe :: Customer . retrieve ( user . stripe_customer_id )
customer . email . should eql user . email
2016-05-12 22:41:25 +00:00
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 )
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
2017-07-17 00:38:40 +00:00
#lesson_session.lesson_booking_slots.length.should eql 3
2016-05-12 22:41:25 +00:00
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 )
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
2017-07-17 00:38:40 +00:00
#booking.lesson_booking_slots.length.should eql 4
2016-05-12 22:41:25 +00:00
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-12 22:41:25 +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
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 )
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-12 22:41:25 +00:00
analysis [ " reason " ] . should eql LessonSessionAnalyser :: STUDENT_FAULT
analysis [ " student " ] . should eql LessonSessionAnalyser :: NO_SHOW
2017-03-22 12:39:06 +00:00
lesson_session . billed . should be_true
2016-05-12 22:41:25 +00:00
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
2017-03-22 12:39:06 +00:00
lesson_session . billing_attempts . should eql 1
2016-05-12 22:41:25 +00:00
user . reload
2017-03-22 12:39:06 +00:00
user . lesson_purchases . length . should eql 1
LessonBooking . hourly_check
lesson_session . reload
teacher_distribution = lesson_session . teacher_distribution
teacher_distribution . amount_in_cents . should eql 3000
teacher_distribution . ready . should be_true
teacher_distribution . distributed . should be_false
lesson_session . teacher_distributions . count . should eql 1
education_distribution = lesson_session . education_distribution
education_distribution . should be_nil
lesson_session . billed . should be true
user . reload
user . lesson_purchases . length . should eql 1
user . sales . length . should eql 1
lesson_session . amount_charged . should eql 32 . 48
2016-05-12 22:41:25 +00:00
lesson_session . billing_error_reason . should be_nil
2017-03-22 12:39:06 +00:00
lesson_session . sent_billing_notices . should be_true
2016-05-12 22:41:25 +00:00
user . reload
user . remaining_test_drives . should eql 0
2017-03-22 12:39:06 +00:00
UserMailer . deliveries . length . should eql 2 # one for student, one for teacher
TeacherPayment . count . should eql 0
TeacherPayment . hourly_check
TeacherPayment . count . should eql 1
2016-05-12 22:41:25 +00:00
2017-03-22 12:39:06 +00:00
LessonPaymentCharge . count . should eql 1
TeacherDistribution . count . should eql 1
teacher_distribution . reload
teacher_distribution . distributed . should be_false
payment = teacher_distribution . teacher_payment
payment . amount_in_cents . should eql 3000
payment . fee_in_cents . should eql ( 3000 * ( school . base_rate + 0 . 03 ) ) . round
payment . teacher_payment_charge . amount_in_cents . should eql ( ( teacher_distribution . amount_in_cents - teacher_distribution . teacher_fee_in_cents ) + ( teacher_distribution . amount_in_cents - teacher_distribution . teacher_fee_in_cents ) * APP_CONFIG . stripe [ :ach_pct ] ) . round
payment . teacher_payment_charge . fee_in_cents . should eql payment . teacher_payment_charge . fee_in_cents
payment . teacher . should eql teacher_user
payment . teacher_distribution . should eql teacher_distribution
lesson_session . lesson_booking . status . should eql LessonBooking :: STATUS_COMPLETED
lesson_session . lesson_booking . success . should be_true
2016-05-12 22:41:25 +00:00
end
2016-05-16 16:39:20 +00:00
2016-09-08 10:59:58 +00:00
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
# make sure can get stripe payments
# get user and teacher into same school
school . education = true
school . save!
user . school = school
user . save!
teacher . school = school
teacher . save!
# user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking . book_normal ( user , teacher_user , valid_single_slots , " Hey I've heard of you before. " , false , LessonBooking :: PAYMENT_STYLE_SINGLE , 60 )
booking . errors . any? . should be_false
booking . school . should be_true
booking . card_presumed_ok . should be_false
booking . user . should eql user
2017-01-17 18:24:49 +00:00
booking . same_school_free . should be_false
#user.unprocessed_normal_lesson.should be_nil
2016-09-08 10:59:58 +00:00
booking . sent_notices . should be_false
booking . booked_price . should eql 30 . 00
booking . is_requested? . should be_true
booking . lesson_sessions [ 0 ] . music_session . scheduled_start . should eql booking . default_slot . scheduled_time ( 0 )
LessonPaymentCharge . count . should eql 1
########## Need validate their credit card
token = create_stripe_token
result = user . payment_update ( { token : token , zip : '78759' , normal : true , booking_id : booking . id } )
booking = result [ :lesson ]
lesson = booking . lesson_sessions [ 0 ]
booking . errors . any? . should be_false
lesson . errors . any? . should be_false
booking . card_presumed_ok . should be_true
booking . sent_notices . should be_true
lesson . music_session . scheduled_start . should eql booking . default_slot . scheduled_time ( 0 )
lesson . amount_charged . should eql 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_single , hour : 14 )
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
2017-07-17 00:38:40 +00:00
#lesson_session.lesson_booking_slots.length.should eql 3
2016-09-08 10:59:58 +00:00
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 )
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
2017-07-17 00:38:40 +00:00
#booking.lesson_booking_slots.length.should eql 4
2016-09-08 10:59:58 +00:00
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 } )
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 )
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
lesson_session . billed . should be_true
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 . billing_attempts . should eql 1
user . reload
user . lesson_purchases . length . should eql 1
LessonBooking . hourly_check
lesson_session . reload
teacher_distribution = lesson_session . teacher_distribution
teacher_distribution . amount_in_cents . should eql 3000
teacher_distribution . ready . should be_true
teacher_distribution . distributed . should be_false
lesson_session . teacher_distributions . count . should eql 2
education_distribution = lesson_session . education_distribution
education_distribution . amount_in_cents . should eql ( 3000 * 0 . 0625 ) . round
education_distribution . ready . should be_true
education_distribution . distributed . should be_false
lesson_session . billed . should be true
user . reload
user . lesson_purchases . length . should eql 1
user . sales . length . should eql 1
lesson_session . amount_charged . should eql 32 . 48
lesson_session . billing_error_reason . should be_nil
lesson_session . sent_billing_notices . should be_true
user . reload
user . remaining_test_drives . should eql 0
UserMailer . deliveries . length . should eql 2 # one for student, one for teacher
TeacherPayment . count . should eql 0
TeacherPayment . hourly_check
TeacherPayment . count . should eql 2
LessonPaymentCharge . count . should eql 1
TeacherDistribution . count . should eql 2
teacher_distribution . reload
teacher_distribution . distributed . should be_true
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
payment = teacher_distribution . teacher_payment
payment . amount_in_cents . should eql 3000
2017-03-22 12:39:06 +00:00
payment . fee_in_cents . should eql ( 3000 * ( school . base_rate + 0 . 03 ) ) . round
2017-01-17 18:24:49 +00:00
payment . teacher_payment_charge . amount_in_cents . should eql ( ( teacher_distribution . amount_in_cents - teacher_distribution . teacher_fee_in_cents ) + ( teacher_distribution . amount_in_cents - teacher_distribution . teacher_fee_in_cents ) * APP_CONFIG . stripe [ :ach_pct ] ) . round
payment . teacher_payment_charge . fee_in_cents . should eql payment . teacher_payment_charge . fee_in_cents
2016-09-08 10:59:58 +00:00
payment . teacher . should eql teacher_user
payment . teacher_distribution . should eql teacher_distribution
lesson_session . lesson_booking . status . should eql LessonBooking :: STATUS_COMPLETED
lesson_session . lesson_booking . success . should be_true
2017-01-17 18:24:49 +00:00
end
it " works (retailer on retailer) " do
# make sure teacher can get payments
teacher . stripe_account_id = stripe_account1_id
retailer . user . stripe_account_id = stripe_account2_id
# make sure can get stripe payments
# get user and teacher into same retailer
teacher_split = 70
retailer_split = 30
teacher_split_pct = ( teacher_split / 100 . 0 )
retailer_split_pct = ( retailer_split / 100 . 0 )
retailer . update_payment ( { teacher : teacher_split , retailer : retailer_split } )
retailer . save!
user . affiliate_referral = retailer . affiliate_partner
user . save!
teacher . retailer = retailer
teacher . save!
# user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking . book_normal ( user , teacher_user , valid_single_slots , " Hey I've heard of you before. " , false , LessonBooking :: PAYMENT_STYLE_SINGLE , 60 )
booking . errors . any? . should be_false
booking . retailer . should eql retailer
booking . card_presumed_ok . should be_false
booking . user . should eql user
booking . same_school_free . should be_false
booking . same_retailer . should be_true
#user.unprocessed_normal_lesson.should be_nil
booking . sent_notices . should be_false
booking . booked_price . should eql 30 . 00
booking . is_requested? . should be_true
booking . lesson_sessions [ 0 ] . music_session . scheduled_start . should eql booking . default_slot . scheduled_time ( 0 )
LessonPaymentCharge . count . should eql 1
########## Need validate their credit card
token = create_stripe_token
result = user . payment_update ( { token : token , zip : '78759' , normal : true , booking_id : booking . id } )
booking = result [ :lesson ]
lesson = booking . lesson_sessions [ 0 ]
booking . errors . any? . should be_false
lesson . errors . any? . should be_false
booking . card_presumed_ok . should be_true
booking . sent_notices . should be_true
lesson . music_session . scheduled_start . should eql booking . default_slot . scheduled_time ( 0 )
lesson . amount_charged . should eql 0 . 0
lesson . reload
2016-09-08 10:59:58 +00:00
2017-01-17 18:24:49 +00:00
user . reload
user . stripe_customer_id . should_not be nil
user . remaining_test_drives . should eql 0
user . lesson_purchases . length . should eql 0
2016-09-08 10:59:58 +00:00
2017-01-17 18:24:49 +00:00
customer = Stripe :: Customer . retrieve ( user . stripe_customer_id )
customer . email . should eql user . email
2016-09-08 10:59:58 +00:00
2017-01-17 18:24:49 +00:00
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 )
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
2017-07-17 00:38:40 +00:00
#lesson_session.lesson_booking_slots.length.should eql 3
2017-01-17 18:24:49 +00:00
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 )
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
2017-07-17 00:38:40 +00:00
#booking.lesson_booking_slots.length.should eql 4
2017-01-17 18:24:49 +00:00
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 } )
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 )
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
lesson_session . billed . should be_true
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 . billing_attempts . should eql 1
user . reload
user . lesson_purchases . length . should eql 1
LessonBooking . hourly_check
lesson_session . reload
lesson_session . education_distribution . should be_nil
teacher_distribution = lesson_session . teacher_distribution
teacher_distribution . amount_in_cents . should eql ( 3000 * teacher_split_pct ) . round
teacher_distribution . teacher_fee_in_cents . should eql ( teacher_distribution . amount_in_cents * ( teacher_split_pct * ( retailer . jamkazam_rate + APP_CONFIG . stripe [ :charge_fee ] ) ) ) . round
teacher_distribution . ready . should be_true
teacher_distribution . distributed . should be_false
lesson_session . teacher_distributions . count . should eql 2
retailer_distribution = lesson_session . retailer_distribution
retailer_distribution . amount_in_cents . should eql ( 3000 * retailer_split_pct ) . round
retailer_distribution . teacher_fee_in_cents . should eql ( retailer_distribution . amount_in_cents * ( retailer_split_pct * ( retailer . jamkazam_rate + APP_CONFIG . stripe [ :charge_fee ] ) ) ) . round
retailer_distribution . ready . should be_true
retailer_distribution . distributed . should be_false
lesson_session . billed . should be true
user . reload
user . lesson_purchases . length . should eql 1
user . sales . length . should eql 1
lesson_session . amount_charged . should eql 32 . 48
lesson_session . billing_error_reason . should be_nil
lesson_session . sent_billing_notices . should be_true
user . reload
user . remaining_test_drives . should eql 0
UserMailer . deliveries . length . should eql 2 # one for student, one for teacher
TeacherPayment . count . should eql 0
TeacherPayment . hourly_check
TeacherPayment . count . should eql 2
LessonPaymentCharge . count . should eql 1
TeacherDistribution . count . should eql 2
teacher_distribution . reload
teacher_distribution . distributed . should be_true
retailer_distribution . reload
retailer_distribution . distributed . should be_true
retailer_amt = ( 3000 * retailer_split_pct ) . round
payment = retailer_distribution . teacher_payment
payment . amount_in_cents . should eql retailer_distribution . amount_in_cents
payment . fee_in_cents . should eql retailer_distribution . teacher_fee_in_cents
payment . teacher_payment_charge . amount_in_cents . should eql ( ( retailer_distribution . amount_in_cents - retailer_distribution . teacher_fee_in_cents ) + ( retailer_distribution . amount_in_cents - retailer_distribution . teacher_fee_in_cents ) * APP_CONFIG . stripe [ :ach_pct ] ) . round
payment . teacher_payment_charge . fee_in_cents . should eql retailer_distribution . teacher_fee_in_cents
payment . teacher . should eql teacher_user
payment . teacher_distribution . should eql retailer_distribution
payment = teacher_distribution . teacher_payment
payment . amount_in_cents . should eql teacher_distribution . amount_in_cents
payment . fee_in_cents . should eql teacher_distribution . teacher_fee_in_cents
payment . teacher_payment_charge . amount_in_cents . should eql ( ( teacher_distribution . amount_in_cents - teacher_distribution . teacher_fee_in_cents ) + ( teacher_distribution . amount_in_cents - teacher_distribution . teacher_fee_in_cents ) * APP_CONFIG . stripe [ :ach_pct ] ) . round
payment . teacher_payment_charge . fee_in_cents . should eql payment . teacher_payment_charge . fee_in_cents
payment . teacher . should eql teacher_user
payment . teacher_distribution . should eql teacher_distribution
lesson_session . lesson_booking . status . should eql LessonBooking :: STATUS_COMPLETED
lesson_session . lesson_booking . success . should be_true
2016-09-08 10:59:58 +00:00
end
2016-05-16 16:39:20 +00:00
it " affiliate gets their cut " do
user . affiliate_referral = affiliate_partner
user . save!
teacher_user . affiliate_referral = affiliate_partner2
teacher_user . save!
2016-05-21 03:13:39 +00:00
lesson = normal_lesson ( user , teacher_user , { accept : true , finish : true } )
2016-05-16 16:39:20 +00:00
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!
2016-05-21 03:13:39 +00:00
lesson = normal_lesson ( user , teacher_user , { accept : true , finish : true } )
lesson . errors . any? . should be_false
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
affiliate_partner . affiliate_distributions . count . should eql 1
partner1_distribution = affiliate_partner . affiliate_distributions . first
2017-03-22 12:39:06 +00:00
partner1_distribution . affiliate_referral_fee_in_cents . should eql ( 3000 * 0 . 30 * affiliate_partner . lesson_rate ) . round
2016-05-16 16:39:20 +00:00
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!
2016-05-21 03:13:39 +00:00
lesson = normal_lesson ( user , teacher_user , { accept : true , finish : 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
affiliate_partner . affiliate_distributions . count . should eql 1
2017-03-22 12:39:06 +00:00
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
2016-05-16 16:39:20 +00:00
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
2016-04-06 02:23:15 +00:00
end
2021-02-01 14:56:47 +00:00
= end