VRFS-3967 - backend teacher/school code working. needs test and some more edge cases

This commit is contained in:
Seth Call 2016-05-12 16:29:27 -05:00
parent 309ebb4e5a
commit cfb8e27850
29 changed files with 449 additions and 127 deletions

View File

@ -346,4 +346,5 @@ lessons_unread_messages.sql
track_school_signups.sql
add_test_drive_types.sql
updated_subjects.sql
update_payment_history.sql
update_payment_history.sql
lesson_booking_schools.sql

View File

@ -0,0 +1,3 @@
ALTER TABLE lesson_bookings ADD COLUMN school_id INTEGER REFERENCES schools(id);
ALTER TABLE teacher_payments ADD COLUMN school_id INTEGER REFERENCES schools(id);
ALTER TABLE teacher_distributions ADD COLUMN school_id INTEGER REFERENCES schools(id);

View File

@ -1270,12 +1270,39 @@ module JamRuby
end
end
# always goes to the teacher
def teacher_distribution_done(teacher_payment)
@school = teacher_payment.school
@teacher_payment = teacher_payment
@distribution = teacher_payment.teacher_distribution
@teacher = teacher_payment.teacher
@payable_teacher = teacher_payment.payable_teacher
@name = @teacher.first_name || 'Anonymous'
@student = @distribution.student
email = @teacher.email
@subject = "You have received payment for your participation in JamClass"
if @school
if @distribution.is_test_drive?
@subject = "Your TestDrive lesson with #{@student.name}"
elsif @distribution.is_normal?
@subject = "Your lesson with #{@student.name}"
elsif @distribution.is_monthly?
@subject = "Your #{@distribution.month_name} lessons with #{@student.name}"
else
@subject = "Your lesson with #{@student.name}"
end
else
if @distribution.is_test_drive?
@subject = "You have earned #{@distribution.real_distribution_display} for your TestDrive lesson with #{@student.first_name}"
elsif @distribution.is_normal?
@subject = "You have earned #{@distribution.real_distribution_display} for your lesson with #{@student.first_name}"
elsif @distribution.is_monthly?
@subject = "You have earned #{@distribution.real_distribution_display} for your #{@distribution.month_name} lessons with #{@student.first_name}"
else
@subject = "You have earned #{@distribution.real_distribution_display} for your lesson with #{@student.first_name}"
end
end
unique_args = {:type => "teacher_distribution_done"}
sendgrid_category "Notification"
@ -1290,24 +1317,51 @@ module JamRuby
end
end
# if school, goes to school owner; otherwise goes to teacher
def teacher_distribution_fail(teacher_payment)
@school = teacher_payment.school
@teacher_payment = teacher_payment
@distribution = teacher_payment.teacher_distribution
@teacher = teacher_payment.teacher
email = @teacher.email
@payable_teacher = teacher_payment.payable_teacher
@student = @distribution.student
@name = @payable_teacher.first_name || 'Anonymous'
email = @payable_teacher.email
@card_declined = teacher_payment.is_card_declined?
@card_expired = teacher_payment.is_card_expired?
@bill_date = teacher_payment.last_billed_at_date
@subject = "We were unable to pay you today"
if @school
if @distribution.is_test_drive?
@subject = "We had a problem paying #{@distribution.real_distribution_display} for #{@teacher.name}'s TestDrive lesson with #{@student.name}"
elsif @distribution.is_normal?
@subject = "We had a problem paying #{@distribution.real_distribution_display} for #{@teacher.name}'s lesson with #{@student.name}"
elsif @distribution.is_monthly?
@subject = "We had a problem paying #{@distribution.real_distribution_display} for #{@teacher.name}'s #{@distribution.month_name} lessons with #{@student.name}"
else
@subject = "We had a problem paying #{@distribution.real_distribution_display} for #{@teacher.name}'s lesson with #{@student.name}"
end
else
if @distribution.is_test_drive?
@subject = "We had a problem paying you #{@distribution.real_distribution_display} for your TestDrive lesson with #{@student.first_name}"
elsif @distribution.is_normal?
@subject = "We had a problem paying you #{@distribution.real_distribution_display} for your lesson with #{@student.first_name}"
elsif @distribution.is_monthly?
@subject = "We had a problem paying you #{@distribution.real_distribution_display} for your #{@distribution.month_name} lessons with #{@student.first_name}"
else
@subject = "We had a problem paying you #{@distribution.real_distribution_display} for your lesson with #{@student.first_name}"
end
end
unique_args = {:type => "teacher_distribution_fail"}
sendgrid_category "Notification"
sendgrid_unique_args :type => unique_args[:type]
sendgrid_recipients([email])
sendgrid_substitute('@USERID', [@teacher.id])
sendgrid_substitute('@USERID', [@payable_teacher.id])
mail(:to => email, :subject => @subject) do |format|
format.text
@ -1315,6 +1369,42 @@ module JamRuby
end
end
# always goes to the school owner
def school_distribution_done(teacher_payment)
@school = teacher_payment.school
@teacher_payment = teacher_payment
@distribution = teacher_payment.teacher_distribution
@teacher = teacher_payment.teacher
@payable_teacher = @school.owner
@name = @payable_teacher.first_name || 'Anonymous'
@student = @distribution.student
email = @payable_teacher.email
if @distribution.is_test_drive?
@subject = "#{@teacher.name} has earned #{@distribution.real_distribution_display} for TestDrive lesson with #{@student.name}"
elsif @distribution.is_normal?
@subject = "#{@teacher.name} has earned #{@distribution.real_distribution_display} for a lesson with #{@student.name}"
elsif @distribution.is_monthly?
@subject = "#{@teacher.name} has earned #{@distribution.real_distribution_display} for #{@distribution.month_name} lessons with #{@student.name}"
else
@subject = "#{@teacher.name} has earned #{@distribution.real_distribution_display} for a lesson with #{@student.name}"
end
unique_args = {:type => "school_distribution_done"}
sendgrid_category "Notification"
sendgrid_unique_args :type => unique_args[:type]
sendgrid_recipients([email])
sendgrid_substitute('@USERID', [@payable_teacher.id])
mail(:to => email, :subject => @subject) do |format|
format.text
format.html
end
end
def monthly_recurring_done(lesson_session)
@student = lesson_session.student
@teacher = lesson_session.teacher

View File

@ -7,7 +7,7 @@
</p>
<p>
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. As just a reminder, you already paid for this lesson in advance.
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>.
</p>
<p>

View File

@ -0,0 +1,20 @@
<% provide(:title, @subject) %>
<p>
Hello <%= @name %>,
</p>
<% if @distribution.is_test_drive? %>
<p>We have processed a payment to you via your Stripe account for $<%= @distribution.real_distribution_display %> for this lesson.</p>
<% elsif @distribution.is_normal? %>
<p>We have processed a payment to you via your Stripe account for $<%= @distribution.real_distribution_display %> for this lesson.</p>
<% elsif @distribution.is_monthly? %>
<p>We have processed a payment to you via your Stripe account for $<%= @distribution.real_distribution_display %> for <%= @distribution.month_name %> lessons.</p>
<% else %>
Unknown payment type.
<% end %>
<br/>
<br/>
Best Regards,<br/>
JamKazam

View File

@ -0,0 +1,16 @@
<% provide(:title, @subject) %>
Hello <%= @name %>,
<% if @distribution.is_test_drive? %>
We have processed a payment to you via your Stripe account for $<%= @distribution.real_distribution_display %> for this lesson.
<% elsif @distribution.is_normal? %>
We have processed a payment to you via your Stripe account for $<%= @distribution.real_distribution_display %> for this lesson.
<% elsif @distribution.is_monthly? %>
We have processed a payment to you via your Stripe account for $<%= @distribution.real_distribution_display %> for <%= @distribution.month_name %> lessons.
<% else %>
Unknown payment type.
<% end %>
Best Regards,
JamKazam

View File

@ -7,8 +7,7 @@
</p>
<p>
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. You have been
billed $<%= @lesson_session.amount_charged %> for today's lesson.
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. You have been billed $<%= @lesson_session.amount_charged %> for today's lesson.
</p>
<p>

View File

@ -1,31 +1,42 @@
<% provide(:title, @subject) %>
<p>You were paid a total of $<%= @teacher_payment.amount %> for your participation in JamClass. Below are more details:</p>
<br/>
<p>
Hello <%= @name %>,
</p>
<% @teacher_payment.teacher_distributions.each do |distribution| %>
<% if distribution.is_test_drive? %>
<h3>You have earned $<%= distribution.amount %> for your TestDrive lesson with <%= distribution.student.name %></h3>
<% if @distribution.is_test_drive? %>
<% if @school %>
<h3>We hope you enjoyed your TestDrive lesson today with <%= @distribution.student.name %>.</h3>
<% else %>
<h3>You have earned $<%= @distribution.real_distribution_display %> for your TestDrive lesson with <%= @distribution.student.name %>.</h3>
<% end %>
<p>
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please <a href="<%= distribution.student.student_ratings_url %>" style="color:#fc0">rate your student</a> now to help us monitor for any issues with students who may cause issues for our instructor community.
<% if !@teacher_payment.teacher.has_rated_student(@distribution.student) %>
If you haven't already done so, please <a href="<%= @distribution.student.student_ratings_url %>" style="color:#fc0">rate your student</a> now to help us monitor for any issues with students who may cause issues for our instructor community.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<% elsif distribution.is_normal? %>
<h3>You have earned $<%= distribution.amount %> for your lesson with <%= distribution.student.name %></h3>
<% elsif @distribution.is_normal? %>
<% if @school %>
<h3>we hope you enjoyed your lesson today with <%= @distribution.student.name %>.</h3>
<% else %>
<h3>You have earned $<%= @distribution.real_distribution_display %> for your lesson with <%= @distribution.student.name %>.</h3>
<% end %>
<p>
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please <a href="<%= distribution.student.student_ratings_url %>" style="color:#fc0">rate your student</a> now to help us monitor for any issues with students who may cause issues for our instructor community.
<% if !@teacher_payment.teacher.has_rated_student(@distribution.student) %>
If you haven't already done so, please <a href="<%= @distribution.student.student_ratings_url %>" style="color:#fc0">rate your student</a> now to help us monitor for any issues with students who may cause issues for our instructor community.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<% elsif distribution.is_monthly? %>
<h3>You have earned $<%= distribution.amount %> for your <%= distribution.month_name%> lesson with <%= distribution.student.name %></h3>
<% elsif @distribution.is_monthly? %>
<% if @school %>
<h3>we hope you enjoyed your <%= @distribution.month_name %> lessons with <%= @distribution.student.name %>.</h3>
<% else %>
<h3>You have earned $<%= @distribution.real_distribution_display %> for your <%= @distribution.month_name%> lessons with <%= @distribution.student.name %>.</h3>
<% end %>
<p>
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please <a href="<%= distribution.student.student_ratings_url %>" style="color:#fc0">rate your student</a> now to help us monitor for any issues with students who may cause issues for our instructor community.
<% if !@teacher_payment.teacher.has_rated_student(@distribution.student) %>
If you haven't already done so, please <a href="<%= @distribution.student.student_ratings_url %>" style="color:#fc0">rate your student</a> now to help us monitor for any issues with students who may cause issues for our instructor community.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
@ -35,7 +46,5 @@
<br/>
<br/>
<% end %>
Best Regards,<br/>
JamKazam

View File

@ -1,31 +1,40 @@
<% provide(:title, @subject) %>
You were paid a total of $<%= @teacher_payment.amount %> for your participation in JamClass. Below are more details:
Hello <%= @name %>,
<% @teacher_payment.teacher_distributions.each do |distribution| %>
<% if distribution.is_test_drive? %>
You have earned $<%= distribution.amount %> for your TestDrive lesson with <%= distribution.student.name %>.
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please rate your student now to help us monitor for any issues with students who may cause issues for our instructor community. <%= distribution.student.student_ratings_url %>
<% if @distribution.is_test_drive? %>
<% if @school %>
We hope you enjoyed your TestDrive lesson today with <%= @distribution.student.name %>.
<% else %>
You have earned $<%= @distribution.amount %> for your TestDrive lesson with <%= @distribution.student.name %>.
<% end %>
<% if !@teacher_payment.teacher.has_rated_student(@distribution.student) %>
If you haven't already done so, please rate your student now to help us monitor for any issues with students who may cause issues for our instructor community. <%= @distribution.student.student_ratings_url %>
<% end%>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at support@jamkazam.com.
<% elsif distribution.is_normal? %>
You have earned $<%= distribution.amount %> for your lesson with <%= distribution.student.name %>.
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please rate your student now to help us monitor for any issues with students who may cause issues for our instructor community. <%= distribution.student.student_ratings_url %>
<% elsif @distribution.is_normal? %>
<% if @school %>
We hope you enjoyed your lesson today with <%= @distribution.student.name %>.
<% else %>
You have earned $<%= @distribution.amount %> for your lesson with <%= @distribution.student.name %>.
<% end %>
<% if !@teacher_payment.teacher.has_rated_student(@distribution.student) %>
If you haven't already done so, please rate your student now to help us monitor for any issues with students who may cause issues for our instructor community. <%= @distribution.student.student_ratings_url %>
<% end%>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at support@jamkazam.com.
<% elsif distribution.is_monthly? %>
You have earned $<%= distribution.amount %> for your <%= distribution.month_name%> lesson with <%= distribution.student.name %>.
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please rate your student now to help us monitor for any issues with students who may cause issues for our instructor community. <%= distribution.student.student_ratings_url %>
<% elsif @distribution.is_monthly? %>
<% if @school %>
We hope you enjoyed your <%= @distribution.month_name%> lessons with <%= @distribution.student.name %>.
<% else %>
You have earned $<%= @distribution.amount %> for your <%= @distribution.month_name%> lessons with <%= @distribution.student.name %>.
<% end %>
<% if !@teacher_payment.teacher.has_rated_student(@distribution.student) %>
If you haven't already done so, please rate your student now to help us monitor for any issues with students who may cause issues for our instructor community. <%= @distribution.student.student_ratings_url %>
<% end%>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at support@jamkazam.com.
<% else %>
Unknown payment type.
<% end %>
<% end %>
Best Regards,
JamKazam

View File

@ -1,5 +1,12 @@
<% provide(:title, @subject) %>
<p>Hello <%= @name %>,</p>
<% if @school %>
<p>
We attempted to process a payment via your Stripe account for <%= @distribution.real_distribution_display %> for this lesson, but the payment failed. Please sign into your Stripe account, and verify that everything there is working properly. Well try again to process this payment in about 24 hours.
</p>
<% else %>
<p>
<% if @card_declined %>
When we tried to distribute a payment to you on <%= @bill_date %>, the charge was declined by stripe. Can you please check your stripe account status? Thank you!
@ -9,6 +16,7 @@
For some reason, when we tried to distribute a payment to you on <%= @bill_date %>, the charge failed. Can you please check your stripe account status? Thank you!
<% end %>
</p>
<% end %>
<br/>
Best Regards,<br/>

View File

@ -1,5 +1,9 @@
<% provide(:title, @subject) %>
Hello <%= @name %>,
<% if @school %>
We attempted to process a payment via your Stripe account for <%= @distribution.real_distribution_display %> for this lesson, but the payment failed. Please sign into your Stripe account, and verify that everything there is working properly. Well try again to process this payment in about 24 hours.
<% else %>
<% if @card_declined %>
When we tried to distribute a payment to you on <%= @bill_date %>, the charge was declined by stripe. Can you please check your stripe account status? Thank you!
<% elsif @card_expired %>
@ -7,6 +11,7 @@ When we tried to distribute a payment to you on <%= @bill_date %>, the charge wa
<% else %>
For some reason, when we tried to distribute a payment to you on <%= @bill_date %>, the charge failed. Can you please check your stripe account status? Thank you!
<% end %>
<% end %>
Best Regards,
JamKazam

View File

@ -1,7 +1,7 @@
module JamRuby
class AffiliatePaymentCharge < Charge
has_one :teacher_payment, class_name: "JamRuby::TeacherPayment", foreign_key: :affiliate_charge_id
#has_one :teacher_payment, class_name: "JamRuby::TeacherPayment", foreign_key: :affiliate_charge_id
def distribution
@distribution ||= teacher_payment.teacher_distribution
@ -36,15 +36,15 @@ module JamRuby
end
def do_send_notices
UserMailer.teacher_distribution_done(teacher_payment)
#UserMailer.teacher_distribution_done(teacher_payment)
end
def do_send_unable_charge
UserMailer.teacher_distribution_fail(teacher_payment)
#UserMailer.teacher_distribution_fail(teacher_payment)
end
def construct_description
teacher_payment.teacher_distribution.description
#teacher_payment.teacher_distribution.description
end
end

View File

@ -98,6 +98,7 @@ module JamRuby
else
self.billing_error_detail = e.to_s + "\n" + e.backtrace.join("\n\t") if e.backtrace
end
puts "Charge: unhandled exception #{billing_error_reason}, #{billing_error_detail}"
self.save(validate: false)
end

View File

@ -39,6 +39,7 @@ module JamRuby
belongs_to :canceler, class_name: "JamRuby::User"
belongs_to :default_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :default_slot_id, inverse_of: :defaulted_booking, :dependent => :destroy
belongs_to :counter_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :counter_slot_id, inverse_of: :countered_booking, :dependent => :destroy
belongs_to :school, class_name: "JamRuby::School"
has_many :lesson_booking_slots, class_name: "JamRuby::LessonBookingSlot", :dependent => :destroy
has_many :lesson_sessions, class_name: "JamRuby::LessonSession", :dependent => :destroy
has_many :lesson_package_purchases, class_name: "JamRuby::LessonPackagePurchase", :dependent => :destroy
@ -61,7 +62,7 @@ module JamRuby
validate :validate_lesson_booking_slots
validate :validate_lesson_length
validate :validate_payment_style
validate :validate_uncollectables
validate :validate_uncollectables, on: :create
validate :validate_accepted, :if => :accepting
validate :validate_canceled, :if => :canceling
@ -657,6 +658,10 @@ module JamRuby
end
end
def school_owned?
!!school
end
def self.book_free(user, teacher, lesson_booking_slots, description)
self.book(user, teacher, LessonBooking::LESSON_TYPE_FREE, lesson_booking_slots, false, 30, PAYMENT_STYLE_ELSEWHERE, description)
end
@ -685,6 +690,9 @@ module JamRuby
lesson_booking.payment_style = payment_style
lesson_booking.description = description
lesson_booking.status = STATUS_REQUESTED
if lesson_booking.teacher && lesson_booking.teacher.teacher.school
lesson_booking.school = school
end
# two-way association slots, for before_validation loic in slot to work
lesson_booking.lesson_booking_slots = lesson_booking_slots

View File

@ -33,7 +33,7 @@ module JamRuby
end
def create_charge
if self.lesson_booking.is_monthly_payment?
if 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
@ -97,8 +97,8 @@ module JamRuby
(price * 100).to_i
end
def description(lesson_booking, time = false)
lesson_package_type.description(lesson_booking, time)
def description(lesson_booking)
lesson_package_type.description(lesson_booking)
end
def stripe_description(lesson_booking)
@ -121,12 +121,21 @@ module JamRuby
user
end
def school_on_school?
teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id)
end
def bill_monthly(force = false)
lesson_payment_charge.charge(force)
if lesson_payment_charge.billed
if school_on_school?
success = true
else
lesson_payment_charge.charge(force)
success = lesson_payment_charge.billed
end
if success
self.sent_notices = true
self.sent_notices_at = Time.now
self.post_processed = true

View File

@ -241,10 +241,14 @@ module JamRuby
end
def bill_lesson
if school_on_school?
success = true
else
lesson_payment_charge.charge
success = lesson_payment_charge.billed
end
lesson_payment_charge.charge
if lesson_payment_charge.billed
if success
self.sent_notices = true
self.sent_notices_at = Time.now
self.post_processed = true
@ -297,9 +301,12 @@ module JamRuby
end
else
if lesson_booking.is_monthly_payment?
# bad session; just poke user
if !sent_notices
UserMailer.monthly_recurring_no_bill(self).deliver
if !school_on_school?
# bad session; just poke user
UserMailer.monthly_recurring_no_bill(self).deliver
end
self.sent_notices = true
self.sent_notices_at = Time.now
self.post_processed = true
@ -309,8 +316,11 @@ module JamRuby
else
if !sent_notices
# bad session; just poke user
UserMailer.student_lesson_normal_no_bill(self).deliver
if !school_on_school?
# bad session; just poke user
UserMailer.student_lesson_normal_no_bill(self).deliver
end
self.sent_notices = true
self.sent_notices_at = Time.now
self.post_processed = true
@ -327,8 +337,11 @@ module JamRuby
bill_lesson
else
if !sent_notices
UserMailer.student_lesson_normal_no_bill(self).deliver
UserMailer.teacher_lesson_normal_no_bill(self).deliver
if !school_on_school?
UserMailer.student_lesson_normal_no_bill(self).deliver
UserMailer.teacher_lesson_normal_no_bill(self).deliver
end
self.sent_notices = true
self.sent_notices_at = Time.now
self.post_processed = true
@ -385,6 +398,10 @@ module JamRuby
@parsed_analysis || analysis ? JSON.parse(analysis) : nil
end
def school_on_school?
teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id)
end
def validate_creating
if !is_requested? && !is_approved?
self.errors.add(:status, "is not valid for a new lesson session.")

View File

@ -69,7 +69,7 @@ module JamRuby
# spec: https://jamkazam.atlassian.net/wiki/display/PS/Product+Specification+-+JamClass#ProductSpecification-JamClass-TeacherReceives&RespondstoLessonBookingRequest
if !force && (!((music_session.scheduled_start + (lesson_session.duration * 60)) < Time.now))
if !force && !((music_session.scheduled_start + (lesson_session.duration * 60)) < Time.now)
reason = SESSION_ONGOING
bill = false
else

View File

@ -18,6 +18,8 @@ module JamRuby
has_many :students, class_name: ::JamRuby::User
has_many :teachers, class_name: ::JamRuby::Teacher
has_many :school_invitations, class_name: 'JamRuby::SchoolInvitation'
has_many :teacher_payments, class_name: 'JamRuby::TeacherPayment'
has_many :teacher_distributions, class_name: 'JamRuby::TeacherDistribution'
validates :user, presence: true
validates :enabled, inclusion: {in: [true, false]}

View File

@ -5,6 +5,7 @@ module JamRuby
belongs_to :teacher_payment, class_name: "JamRuby::TeacherPayment"
belongs_to :lesson_session, class_name: "JamRuby::LessonSession"
belongs_to :lesson_package_purchase, class_name: "JamRuby::LessonPackagePurchase"
belongs_to :school, class_name: "JamRuby::School"
validates :teacher, presence: true
validates :amount_in_cents, presence: true
@ -14,7 +15,7 @@ module JamRuby
limit ||= 100
limit = limit.to_i
query = TeacherDistribution.where(teacher_id: current_user.id).order('created_at desc')
query = TeacherDistribution.where(teacher_id: current_user.id).where('school_id IS NULL').order('created_at desc')
current_page = params[:page].nil? ? 1 : params[:page].to_i
next_page = current_page + 1
@ -59,6 +60,7 @@ module JamRuby
distribution.ready = false
distribution.distributed = false
distribution.amount_in_cents = target.lesson_booking.distribution_price_in_cents(target)
distribution.school = target.lesson_booking.school
distribution
end
@ -70,11 +72,19 @@ module JamRuby
amount_in_cents - calculate_teacher_fee
end
def real_distribution
(real_distribution_in_cents / 100.0)
end
def real_distribution_display
'$%.2f' % real_distribution
end
def calculate_teacher_fee
if is_test_drive?
0
else
(amount_in_cents * teacher.teacher.jamkazam_rate + 0.03).round
(amount_in_cents * (teacher.teacher.jamkazam_rate + 0.03)).round
end
end

View File

@ -4,12 +4,22 @@ module JamRuby
belongs_to :teacher, class_name: "JamRuby::User", foreign_key: :teacher_id
belongs_to :teacher_payment_charge, class_name: "JamRuby::TeacherPaymentCharge", foreign_key: :charge_id
has_one :teacher_distribution, class_name: "JamRuby::TeacherDistribution"
belongs_to :school, class_name: "JamRuby::School"
def self.hourly_check
teacher_payments
end
# pay the school if the payment owns the school; otherwise default to the teacher
def payable_teacher
if school
school.owner
else
teacher
end
end
def teacher_distributions
[teacher_distribution]
end
@ -74,13 +84,13 @@ module JamRuby
payment.teacher_distribution = teacher_distribution
end
payment.school = payment.teacher_distribution.school
payment.amount_in_cents = payment.teacher_distribution.amount_in_cents
payment.fee_in_cents = payment.teacher_distribution.calculate_teacher_fee
if payment.teacher_payment_charge.nil?
charge = TeacherPaymentCharge.new
charge.user = teacher
charge.user = payment.payable_teacher
charge.amount_in_cents = payment.amount_in_cents
charge.fee_in_cents = payment.fee_in_cents
charge.teacher_payment = payment

View File

@ -12,15 +12,13 @@ module JamRuby
end
def teacher
@teacher ||= teacher_payment.teacher
@teacher ||= teacher_payment.payable_teacher
end
def charged_user
teacher
end
def do_charge(force)
# source will let you supply a token. But... how to get a token in this case?
@ -38,11 +36,17 @@ module JamRuby
end
def do_send_notices
UserMailer.teacher_distribution_done(teacher_payment)
unless teacher_payment.school && distribution.is_monthly?
# we don't send monthly success notices to the teacher if they are in a school, otherwise they get an email
UserMailer.teacher_distribution_done(teacher_payment).deliver
end
if teacher_payment.school
UserMailer.school_distribution_done(teacher_payment).deliver
end
end
def do_send_unable_charge
UserMailer.teacher_distribution_fail(teacher_payment)
UserMailer.teacher_distribution_fail(teacher_payment).deliver
end
def construct_description

View File

@ -38,11 +38,11 @@ describe "Monthly Recurring Lesson Flow" do
########## Need validate their credit card
token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', normal: true})
booking = result[:lesson]
lesson = booking.lesson_sessions[0]
booking.card_presumed_ok.should be_true
booking.errors.any?.should be_false
lesson.errors.any?.should be_false
booking.card_presumed_ok.should be_true
booking = result[:lesson]
lesson = booking.lesson_sessions[0]
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

View File

@ -14,7 +14,9 @@ describe "Normal Lesson Flow" do
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
describe "stripe mocked" do
before { StripeMock.start
before {
StripeMock.clear_errors
StripeMock.start
teacher.stripe_account_id = stripe_account1_id
teacher.save!
}
@ -35,7 +37,7 @@ describe "Normal Lesson Flow" do
########## Need validate their credit card
token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', normal: true})
result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
booking = result[:lesson]
lesson = booking.lesson_sessions[0]
booking.errors.any?.should be_false
@ -92,6 +94,7 @@ describe "Normal Lesson Flow" do
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
@ -178,12 +181,12 @@ describe "Normal Lesson Flow" do
lesson_session.reload
payment = lesson_session.lesson_payment_charge
payment.amount_in_cents.should eql 3248
payment.fee_in_cents.should eql 0
lesson_session.billing_attempts.should eql 4
lesson_session.post_processed.should be_true
LessonPaymentCharge.count.should eql 2
LessonPaymentCharge.count.should eql 1
lesson_session.reload
lesson_session.analysed.should be_true
@ -267,7 +270,7 @@ describe "Normal Lesson Flow" do
########## Need validate their credit card
token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', normal: true})
result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
booking = result[:lesson]
lesson = booking.lesson_sessions[0]
booking.errors.any?.should be_false
@ -373,6 +376,7 @@ describe "Normal Lesson Flow" do
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

View File

@ -13,6 +13,9 @@ describe "Recurring Lesson Flow" do
let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] }
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
before(:each) do
Timecop.return
end
it "works" do
# user has no test drives, no credit card on file, but attempts to book a lesson
@ -27,7 +30,7 @@ describe "Recurring Lesson Flow" do
########## Need validate their credit card
token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', normal: true})
result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
booking = result[:lesson]
lesson = booking.lesson_sessions[0]
booking.errors.any?.should be_false
@ -139,6 +142,7 @@ describe "Recurring Lesson Flow" do
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

View File

@ -14,6 +14,7 @@ describe "TestDrive Lesson Flow" do
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
before {
Timecop.return
teacher.stripe_account_id = stripe_account1_id
teacher.save!
}
@ -163,6 +164,7 @@ describe "TestDrive Lesson Flow" do
lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save!
Timecop.travel(end_time + 1)
UserMailer.deliveries.clear

View File

@ -137,7 +137,7 @@ describe LessonBooking do
end
it "advances to next month" do
user.card_approved(create_stripe_token, '78759', booking.id)
user.card_approved(create_stripe_token, '78759', nil)
user.save!
day = Date.new(2016, 1, 20)
@ -386,38 +386,6 @@ describe LessonBooking do
end
describe "book_free" do
it "works" do
pending "free not supported"
booking = LessonBooking.book_free(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
booking.errors.any?.should be false
booking.user.should eq user
booking.teacher.should eq teacher_user
booking.description.should eq ("Hey I've heard of you before.")
booking.payment_style.should eq LessonBooking::PAYMENT_STYLE_ELSEWHERE
booking.recurring.should eq false
booking.lesson_length.should eq 30
booking.lesson_type.should eq LessonBooking::LESSON_TYPE_FREE
booking.lesson_booking_slots.length.should eq 2
chat_message = ChatMessage.where(lesson_booking_id: booking.id).first
chat_message.should_not be_nil
chat_message.message.should eq booking.description
user.reload
user.remaining_free_lessons.should eq 0
user.remaining_test_drives.should eq 1
booking.card_presumed_ok.should eq false
booking.sent_notices.should eq false
user.card_approved(create_stripe_token, '78759')
user.save!
booking.reload
booking.sent_notices.should eq true
booking.card_presumed_ok.should eq true
end
it "allows long message to flow through chat" do
@ -446,16 +414,6 @@ describe LessonBooking do
ChatMessage.count.should eq 1
end
it "prevents user without stored credit card" do
pending "free not supported"
user.stored_credit_card = false
user.save!
booking = LessonBooking.book_free(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
booking.errors.any?.should be false
end
it "must have 2 lesson booking slots" do
booking = LessonBooking.book_test_drive(user, teacher_user, [], "Hey I've heard of you before.")
@ -520,7 +478,7 @@ describe LessonBooking do
booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
booking.errors.any?.should be true
booking.errors[:user].should eq ["has a requested TestDrive with this teacher"]
booking.errors[:user].should eq ["have a requested TestDrive with this teacher"]
ChatMessage.count.should eq 1
end

View File

@ -2,7 +2,7 @@ require 'spec_helper'
describe LessonSession do
let(:user) {FactoryGirl.create(:user, stored_credit_card: false, remaining_free_lessons: 1, remaining_test_drives: 1)}
let(:user) {FactoryGirl.create(:user, stored_credit_card: true, remaining_free_lessons: 1, remaining_test_drives: 1)}
let(:teacher) {FactoryGirl.create(:teacher_user)}
let(:slot1) { FactoryGirl.build(:lesson_booking_slot_single) }
let(:slot2) { FactoryGirl.build(:lesson_booking_slot_single) }

View File

@ -9,6 +9,9 @@ describe Sale do
let(:jam_track3) { FactoryGirl.create(:jam_track) }
let(:gift_card) { GiftCardType.jam_track_5 }
before(:each) {
Timecop.return
}
def assert_free_line_item(sale_line_item, jamtrack)
sale_line_item.recurly_tax_in_cents.should be_nil
sale_line_item.recurly_total_in_cents.should be_nil
@ -596,6 +599,8 @@ describe Sale do
lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save!
Timecop.travel(end_time + 1)
# bill the user
LessonSession.hourly_check
@ -649,6 +654,8 @@ describe Sale do
lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save!
Timecop.travel(end_time + 1)
# bill the user
LessonSession.hourly_check
@ -715,7 +722,7 @@ describe Sale do
booking.should eql user.unprocessed_test_drive
token = create_stripe_token
result = user.payment_update({token: token, zip: '72205', test_drive: true})
result = user.payment_update({token: token, zip: '72205', test_drive: true, booking_id: booking.id})
booking.reload
booking.card_presumed_ok.should be_true
@ -762,7 +769,7 @@ describe Sale do
user.remaining_test_drives.should eql 0
token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', test_drive: true})
result = user.payment_update({token: token, zip: '78759', test_drive: true, booking_id: booking.id})
booking.reload
booking.card_presumed_ok.should be_true

View File

@ -6,14 +6,17 @@ describe TeacherPayment do
let(:user2) { FactoryGirl.create(:user) }
let(:teacher_obj) {FactoryGirl.create(:teacher, stripe_account_id: stripe_account1_id)}
let(:teacher_obj2) {FactoryGirl.create(:teacher, stripe_account_id: stripe_account2_id)}
let(:school_owner_teacher) {FactoryGirl.create(:teacher, stripe_account_id: stripe_account2_id)}
let(:teacher) { FactoryGirl.create(:user, teacher: teacher_obj) }
let(:teacher2) { FactoryGirl.create(:user, teacher: teacher_obj2) }
let(:school_teacher) { FactoryGirl.create(:user, teacher: school_owner_teacher)}
let(:test_drive_lesson) {testdrive_lesson(user, teacher)}
let(:test_drive_lesson2) {testdrive_lesson(user2, teacher2)}
let(:test_drive_distribution) {FactoryGirl.create(:teacher_distribution, lesson_session: test_drive_lesson, teacher: teacher, teacher_payment: nil, ready:false)}
let(:test_drive_distribution2) {FactoryGirl.create(:teacher_distribution, lesson_session: test_drive_lesson2, teacher: teacher2, teacher_payment: nil, ready:false)}
let(:normal_lesson_session) {normal_lesson(user, teacher)}
let(:normal_distribution) {FactoryGirl.create(:teacher_distribution, lesson_session: normal_lesson_session, teacher: teacher, teacher_payment: nil, ready:false)}
let(:school) {FactoryGirl.create(:school, user: school_teacher)}
describe "pending_teacher_payments" do
@ -35,6 +38,21 @@ describe TeacherPayment do
payments[0]['id'].should eql teacher.id
end
it "school distribution" do
test_drive_distribution.school = school
test_drive_distribution.save!
payments = TeacherPayment.pending_teacher_payments
payments.count.should eql 0
test_drive_distribution.ready = true
test_drive_distribution.save!
payments = TeacherPayment.pending_teacher_payments
payments.count.should eql 1
payments[0]['id'].should eql teacher.id
end
it "multiple teachers" do
test_drive_distribution.touch
test_drive_distribution2.touch
@ -95,6 +113,7 @@ describe TeacherPayment do
normal_distribution.ready = true
normal_distribution.save!
UserMailer.deliveries.clear
TeacherPayment.teacher_payments
@ -108,9 +127,13 @@ describe TeacherPayment do
puts payment.teacher_payment_charge.billing_error_reason
puts payment.teacher_payment_charge.billing_error_detail
end
# only one confirm email to teacher
UserMailer.deliveries.length.should eql 1
payment.teacher_payment_charge.billed.should eql true
payment.teacher_payment_charge.amount_in_cents.should eql 1000
payment.teacher_payment_charge.fee_in_cents.should eql 280
payment.teacher_payment_charge.teacher.should eql teacher
teacher_distribution = payment.teacher_payment_charge.distribution
teacher_distribution.amount_in_cents.should eql 1000
charge = Stripe::Charge.retrieve(payment.teacher_payment_charge.stripe_charge_id)
@ -118,6 +141,39 @@ describe TeacherPayment do
charge.application_fee.should include("fee_")
end
it "charges school" do
normal_distribution.school = school
normal_distribution.ready = true
normal_distribution.save!
UserMailer.deliveries.clear
TeacherPayment.teacher_payments
normal_distribution.reload
normal_distribution.teacher_payment.should_not be_nil
normal_distribution.teacher_payment.school.should eql school
TeacherPayment.count.should eql 1
payment = normal_distribution.teacher_payment
if payment.teacher_payment_charge.billing_error_reason
puts payment.teacher_payment_charge.billing_error_reason
puts payment.teacher_payment_charge.billing_error_detail
end
# one to school owner, one to teacher
UserMailer.deliveries.length.should eql 2
payment.teacher_payment_charge.billed.should eql true
payment.teacher_payment_charge.amount_in_cents.should eql 1000
payment.teacher_payment_charge.fee_in_cents.should eql 280
payment.teacher_payment_charge.user.should eql school.owner
teacher_distribution = payment.teacher_payment_charge.distribution
teacher_distribution.amount_in_cents.should eql 1000
charge = Stripe::Charge.retrieve(payment.teacher_payment_charge.stripe_charge_id)
charge.destination.should eql school.owner.teacher.stripe_account_id
charge.amount.should eql 1000
charge.application_fee.should include("fee_")
end
it "charges multiple" do
test_drive_distribution.touch
test_drive_distribution.ready = true
@ -240,6 +296,76 @@ describe TeacherPayment do
charge.amount.should eql 1000
end
it "failed payment, then success (school)" do
StripeMock.prepare_card_error(:card_declined)
normal_distribution.school = school
normal_distribution.ready = true
normal_distribution.save!
TeacherPayment.teacher_payments
normal_distribution.reload
normal_distribution.teacher_payment.should_not be_nil
TeacherPayment.count.should eql 1
payment = normal_distribution.teacher_payment
payment.teacher_payment_charge.billing_error_reason.should eql("card_declined")
payment.teacher_payment_charge.billing_error_detail.should include("declined")
payment.teacher_payment_charge.billed.should eql false
payment.teacher_payment_charge.amount_in_cents.should eql 1000
payment.teacher_payment_charge.fee_in_cents.should eql 280
teacher_distribution = payment.teacher_payment_charge.distribution
teacher_distribution.amount_in_cents.should eql 1000
payment.teacher_payment_charge.stripe_charge_id.should be_nil
StripeMock.clear_errors
TeacherPayment.teacher_payments
normal_distribution.reload
normal_distribution.teacher_payment.should_not be_nil
TeacherPayment.count.should eql 1
# make sure the teacher_payment is reused, and charge is reused
normal_distribution.teacher_payment.should eql(payment)
normal_distribution.teacher_payment.teacher_payment_charge.should eql(payment.teacher_payment_charge)
# no attempt should be made because a day hasn't gone by
payment = normal_distribution.teacher_payment
payment.teacher_payment_charge.billed.should eql false
payment.teacher_payment_charge.amount_in_cents.should eql 1000
payment.teacher_payment_charge.fee_in_cents.should eql 280
teacher_distribution = payment.teacher_payment_charge.distribution
teacher_distribution.amount_in_cents.should eql 1000
# advance one day so that a charge is attempted again
Timecop.freeze(Date.today + 2)
TeacherPayment.teacher_payments
normal_distribution.reload
normal_distribution.teacher_payment.should_not be_nil
TeacherPayment.count.should eql 1
# make sure the teacher_payment is reused, and charge is reused
normal_distribution.teacher_payment.should eql(payment)
normal_distribution.teacher_payment.teacher_payment_charge.should eql(payment.teacher_payment_charge)
# no attempt should be made because a day hasn't gone by
payment = normal_distribution.teacher_payment
payment.reload
payment.teacher_payment_charge.billed.should eql true
payment.teacher_payment_charge.amount_in_cents.should eql 1000
payment.teacher_payment_charge.fee_in_cents.should eql 280
teacher_distribution = payment.teacher_payment_charge.distribution
teacher_distribution.amount_in_cents.should eql 1000
charge = Stripe::Charge.retrieve(payment.teacher_payment_charge.stripe_charge_id)
charge.amount.should eql 1000
end
it "charges multiple (with initial failure)" do
StripeMock.prepare_card_error(:card_declined)