From 94128e7115038d12de6f5904d643fef6bae6ab75 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 12 May 2016 17:41:25 -0500 Subject: [PATCH] * relax user id on charges --- db/up/update_payment_history.sql | 2 +- ruby/lib/jam_ruby/models/lesson_booking.rb | 9 +- .../models/lesson_package_purchase.rb | 2 +- ruby/lib/jam_ruby/models/lesson_session.rb | 2 +- .../spec/jam_ruby/flows/normal_lesson_spec.rb | 164 ++++++++++++++++++ .../mixins/PostProcessorMixin.js.coffee | 3 +- web/app/views/api_lesson_sessions/show.rabl | 2 +- 7 files changed, 176 insertions(+), 8 deletions(-) diff --git a/db/up/update_payment_history.sql b/db/up/update_payment_history.sql index f81d81339..0e3e345ca 100644 --- a/db/up/update_payment_history.sql +++ b/db/up/update_payment_history.sql @@ -1 +1 @@ -ALTER TABLE charges ADD COLUMN user_id VARCHAR(64) REFERENCES users(id) NOT NULL; \ No newline at end of file +ALTER TABLE charges ADD COLUMN user_id VARCHAR(64) REFERENCES users(id); \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/lesson_booking.rb b/ruby/lib/jam_ruby/models/lesson_booking.rb index dd373790d..7e884fef4 100644 --- a/ruby/lib/jam_ruby/models/lesson_booking.rb +++ b/ruby/lib/jam_ruby/models/lesson_booking.rb @@ -87,7 +87,7 @@ module JamRuby end def after_create - if card_presumed_ok && !sent_notices + if (card_presumed_ok || school_on_school?) && !sent_notices send_notices end end @@ -691,7 +691,7 @@ module JamRuby lesson_booking.description = description lesson_booking.status = STATUS_REQUESTED if lesson_booking.teacher && lesson_booking.teacher.teacher.school - lesson_booking.school = school + lesson_booking.school = lesson_booking.teacher.teacher.school end # two-way association slots, for before_validation loic in slot to work @@ -710,13 +710,16 @@ module JamRuby end def self.unprocessed(current_user) - LessonBooking.where(user_id: current_user.id).where(card_presumed_ok: false) + LessonBooking.where(user_id: current_user.id).where(card_presumed_ok: false).where('school_id IS NULL') end def self.requested(current_user) LessonBooking.where(user_id: current_user.id).where(status: STATUS_REQUESTED) end + def school_on_school? + teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id) + end def self.find_bookings_needing_sessions(minimum_start_time) MusicSession.select([:lesson_booking_id]).joins(:lesson_session => :lesson_booking).where("lesson_bookings.active = true").where('lesson_bookings.recurring = true').where("scheduled_start is not null").where("scheduled_start > ?", minimum_start_time).group(:lesson_booking_id).having('count(lesson_booking_id) < 2') diff --git a/ruby/lib/jam_ruby/models/lesson_package_purchase.rb b/ruby/lib/jam_ruby/models/lesson_package_purchase.rb index 10405a9e9..66266028f 100644 --- a/ruby/lib/jam_ruby/models/lesson_package_purchase.rb +++ b/ruby/lib/jam_ruby/models/lesson_package_purchase.rb @@ -33,7 +33,7 @@ module JamRuby end def create_charge - if lesson_booking && lesson_booking.is_monthly_payment? + if !school_on_school? && lesson_booking && lesson_booking.is_monthly_payment? self.lesson_payment_charge = LessonPaymentCharge.new lesson_payment_charge.user = user lesson_payment_charge.amount_in_cents = 0 diff --git a/ruby/lib/jam_ruby/models/lesson_session.rb b/ruby/lib/jam_ruby/models/lesson_session.rb index 74574cb00..c85624e77 100644 --- a/ruby/lib/jam_ruby/models/lesson_session.rb +++ b/ruby/lib/jam_ruby/models/lesson_session.rb @@ -75,7 +75,7 @@ module JamRuby scope :past_cancel_window, -> { joins(:music_session).where('music_sessions.scheduled_start > ?', 24.hours.from_now) } def create_charge - if !is_test_drive? && !is_monthly_payment? + if !school_on_school? && !is_test_drive? && !is_monthly_payment? self.lesson_payment_charge = LessonPaymentCharge.new lesson_payment_charge.user = @assigned_student lesson_payment_charge.amount_in_cents = 0 diff --git a/ruby/spec/jam_ruby/flows/normal_lesson_spec.rb b/ruby/spec/jam_ruby/flows/normal_lesson_spec.rb index a81e686be..404978968 100644 --- a/ruby/spec/jam_ruby/flows/normal_lesson_spec.rb +++ b/ruby/spec/jam_ruby/flows/normal_lesson_spec.rb @@ -12,6 +12,7 @@ describe "Normal Lesson Flow" do let(:lesson_booking_slot_recurring2) { FactoryGirl.build(:lesson_booking_slot_recurring) } let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] } let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] } + let(:school) {FactoryGirl.create(:school)} describe "stripe mocked" do before { @@ -417,4 +418,167 @@ describe "Normal Lesson Flow" do user.remaining_test_drives.should eql 0 UserMailer.deliveries.length.should eql 2 # one for student, one for teacher end + + + it "works (school on school)" do + + # get user and teacher into same school + 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 + user.unprocessed_normal_lesson.should eql [] + booking.sent_notices.should be_false + booking.booked_price.should eql 30.00 + booking.is_requested?.should be_true + booking.sent_notices.should be_true + lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) + LessonPaymentCharge.count.should eql 0 + + 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 + booking.lesson_booking_slots.length.should eql 3 + UserMailer.deliveries.length.should eql 1 + chat = ChatMessage.unscoped.order(:created_at).last + chat.channel.should eql ChatMessage::CHANNEL_LESSON + chat.message.should eql 'Does this work?' + chat.user.should eql teacher_user + chat.target_user.should eql user + notification = Notification.unscoped.order(:created_at).last + notification.session_id.should eql lesson_session.music_session.id + notification.student_directed.should eql true + notification.purpose.should eql 'counter' + notification.description.should eql NotificationTypes::LESSON_MESSAGE + + ######### Student counters with new slot + student_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 16) + 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}) + 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 = JSON.parse(lesson_session.analysis) + analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT + analysis["student"].should eql LessonSessionAnalyser::NO_SHOW + if lesson_session.billing_error_detail + puts "testdrive flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests + end + lesson_session.billed.should be true + 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 + UserMailer.deliveries.length.should eql 2 # one for student, one for teacher + + LessonPaymentCharge.count.should eql 0 + TeacherDistribution.count.should eql 0 + end end diff --git a/web/app/assets/javascripts/react-components/mixins/PostProcessorMixin.js.coffee b/web/app/assets/javascripts/react-components/mixins/PostProcessorMixin.js.coffee index 2d6db86ee..11ff2e2ee 100644 --- a/web/app/assets/javascripts/react-components/mixins/PostProcessorMixin.js.coffee +++ b/web/app/assets/javascripts/react-components/mixins/PostProcessorMixin.js.coffee @@ -17,7 +17,8 @@ teacherActions = window.JK.Actions.Teacher lesson.me = me lesson.other = other lesson.isAdmin = context.JK.currentUserAdmin - lesson.cardNotOk = !lesson.lesson_booking.card_presumed_ok + lesson.schoolOnSchool = lesson['school_on_school?'] + lesson.cardNotOk = !lesson.schoolOnSchool && !lesson.lesson_booking.card_presumed_ok if (lesson.status == 'requested' || lesson.status == 'countered') lesson.isRequested = true diff --git a/web/app/views/api_lesson_sessions/show.rabl b/web/app/views/api_lesson_sessions/show.rabl index 31be0b3bc..f10b404fd 100644 --- a/web/app/views/api_lesson_sessions/show.rabl +++ b/web/app/views/api_lesson_sessions/show.rabl @@ -2,7 +2,7 @@ object @lesson_session attributes :id, :lesson_booking_id, :lesson_type, :duration, :price, :teacher_complete, :student_complete, :status, :student_canceled, :teacher_canceled, :student_canceled_at, :teacher_canceled_at, :student_canceled_reason, - :teacher_canceled_reason, :status, :success, :teacher_unread_messages, :student_unread_messages, :is_active?, :recurring, :analysed + :teacher_canceled_reason, :status, :success, :teacher_unread_messages, :student_unread_messages, :is_active?, :recurring, :analysed, :school_on_school? node do |lesson_session| {