* wip
This commit is contained in:
parent
f587eb7260
commit
e2b7eec291
|
|
@ -41,13 +41,14 @@ CREATE TABLE lesson_package_purchases (
|
|||
sent_billing_notices BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
sent_billing_notices_at TIMESTAMP,
|
||||
last_billing_attempt_at TIMESTAMP,
|
||||
success BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
billed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
billed_at TIMESTAMP,
|
||||
billing_error_reason VARCHAR,
|
||||
billing_error_detail VARCHAR,
|
||||
billing_should_retry BOOLEAN NOT NULL DEFAULT TRUE ,
|
||||
billing_attempts INTEGER NOT NULL DEFAULT 0,
|
||||
post_processed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
post_processed_at TIMESTAMP,
|
||||
|
||||
lesson_booking_id VARCHAR(64) REFERENCES lesson_bookings(id) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
|
|
|||
|
|
@ -1166,5 +1166,65 @@
|
|||
format.html { render :layout => "from_user_mailer" }
|
||||
end
|
||||
end
|
||||
|
||||
def student_lesson_monthly_charged(lesson_package_purchase)
|
||||
lesson_booking = lesson_package_purchase.lesson_booking
|
||||
@student = lesson_booking.student
|
||||
@teacher = lesson_booking.teacher
|
||||
@lesson_package_purchase = lesson_package_purchase
|
||||
@card_declined = lesson_package_purchase.is_card_declined?
|
||||
@card_expired = lesson_package_purchase.is_card_expired?
|
||||
@bill_date = lesson_package_purchase.last_billed_at_date
|
||||
@lesson_booking = lesson_booking
|
||||
@month_name = lesson_package_purchase.month_name
|
||||
email = @student.email
|
||||
@subject = "Your JamClass lessons with #{teacher.first_name} for #{@month_name}"
|
||||
|
||||
unique_args = {:type => "student_lesson_monthly_charged"}
|
||||
|
||||
sendgrid_category "Notification"
|
||||
sendgrid_unique_args :type => unique_args[:type]
|
||||
|
||||
sendgrid_recipients([email])
|
||||
sendgrid_substitute('@USERID', [@student.id])
|
||||
|
||||
mail(:to => email, :subject => @subject) do |format|
|
||||
format.text
|
||||
format.html { render :layout => "from_user_mailer" }
|
||||
end
|
||||
end
|
||||
|
||||
def teacher_lesson_monthly_charged(lesson_package_purchase)
|
||||
lesson_booking = lesson_package_purchase.lesson_booking
|
||||
@student = lesson_booking.student
|
||||
@teacher = lesson_booking.teacher
|
||||
@lesson_package_purchase = lesson_package_purchase
|
||||
@card_declined = lesson_package_purchase.is_card_declined?
|
||||
@card_expired = lesson_package_purchase.is_card_expired?
|
||||
@bill_date = lesson_package_purchase.last_billed_at_date
|
||||
@lesson_booking = lesson_booking
|
||||
@month_name = lesson_package_purchase.month_name
|
||||
|
||||
email = @teacher.email
|
||||
if lesson_booking.is_suspended?
|
||||
@subject = "Your weekly lessons with #{@student.name} has been suspended."
|
||||
else
|
||||
@subject = "The student #{@student.name} had a failed credit card charge for #{@month_name}."
|
||||
end
|
||||
|
||||
|
||||
unique_args = {:type => "student_lesson_monthly_charged"}
|
||||
|
||||
sendgrid_category "Notification"
|
||||
sendgrid_unique_args :type => unique_args[:type]
|
||||
|
||||
sendgrid_recipients([email])
|
||||
sendgrid_substitute('@USERID', [@teacher.id])
|
||||
|
||||
mail(:to => email, :subject => @subject) do |format|
|
||||
format.text
|
||||
format.html { render :layout => "from_user_mailer" }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
<% provide(:title, @subject) %>
|
||||
<% provide(:photo_url, @teacher.resolved_photo_url) %>
|
||||
|
||||
<% content_for :note do %>
|
||||
<p>
|
||||
Hello <%= @student.name %>,
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You have been billed $<%= @lesson_package_purchase.amount_charged %> for this month's lessons with <%= @teacher.name %>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<% if !@student.has_rated_teacher(@teacher) %>
|
||||
If you haven't already done so, please <a href="<%= @teacher.ratings_url %>" style="color:#fc0">rate your teacher</a> now to help other students in the community find the best
|
||||
instructors.
|
||||
<% 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>
|
||||
<br/>
|
||||
<p>
|
||||
Best Regards,<br>Team JamKazam
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<p>
|
||||
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. You have been
|
||||
billed <%= @lesson_session.amount_charged %> for today's lesson.
|
||||
billed $<%= @lesson_session.amount_charged %> for today's lesson.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Hello <%= @student.name %>,
|
||||
|
||||
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.
|
||||
|
||||
<% if !@student.has_rated_teacher(@teacher) %>
|
||||
If you haven't already done so, please rate your teacher now to help other students in the community find the best
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ module JamRuby
|
|||
sync_remaining_test_drives
|
||||
|
||||
@default_slot_did_change = nil
|
||||
@accepting = nil
|
||||
@countering = nil
|
||||
end
|
||||
|
||||
def student
|
||||
|
|
@ -306,6 +308,10 @@ module JamRuby
|
|||
status == STATUS_APPROVED
|
||||
end
|
||||
|
||||
def is_suspended?
|
||||
status == STATUS_SUSPENDED
|
||||
end
|
||||
|
||||
def validate_accepted
|
||||
if !is_requested?
|
||||
self.errors.add(:status, "This lesson is already #{self.status}.")
|
||||
|
|
@ -393,7 +399,7 @@ module JamRuby
|
|||
# errors.add(:user, 'has no credit card stored')
|
||||
#end
|
||||
elsif is_test_drive?
|
||||
if !user.has_test_drives? && !user.can_buy_test_drive?
|
||||
if !user.has_test_drives? || !user.can_buy_test_drive?
|
||||
errors.add(:user, "have no remaining test drives")
|
||||
end
|
||||
elsif is_normal?
|
||||
|
|
@ -513,7 +519,7 @@ module JamRuby
|
|||
LessonBooking
|
||||
.joins(:lesson_sessions)
|
||||
.joins("LEFT JOIN lesson_package_purchases ON (lesson_package_purchases.lesson_booking_id = lesson_bookings.id AND (lesson_package_purchases.year = #{current_month_first_day.year} AND lesson_package_purchases.month = #{current_month_first_day.month}) OR (lesson_package_purchases.year = #{next_month_last_day.year} AND lesson_package_purchases.month = #{next_month_last_day.month}))")
|
||||
.where("lesson_package_purchases.id IS NULL OR (lesson_package_purchases.success == false)")
|
||||
.where("lesson_package_purchases.id IS NULL OR (lesson_package_purchases.id IS NOT NULL AND lesson_package_purchases.post_processed = false)")
|
||||
.where(payment_style: PAYMENT_STYLE_MONTHLY)
|
||||
.where(status: STATUS_APPROVED)
|
||||
.where('lesson_sessions.created_at >= ?', current_month_first_day)
|
||||
|
|
@ -543,8 +549,17 @@ module JamRuby
|
|||
end
|
||||
|
||||
def suspend!
|
||||
# when this is called, the calling code sends out a email to let the student and teacher know (it feels unnatural it's not here, though)
|
||||
self.status = STATUS_SUSPENDED
|
||||
self.save
|
||||
if self.save
|
||||
future_sessions.each do |lesson_session|
|
||||
lesson_session.suspend!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def future_sessions
|
||||
lesson_sessions.joins(:music_session).where('scheduled_start > ?', Time.now).order(:created_at)
|
||||
end
|
||||
|
||||
def self.schedule_upcoming_lessons
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ module JamRuby
|
|||
|
||||
end
|
||||
|
||||
def amount_charged
|
||||
sale_line_item.sale.recurly_total_in_cents / 100.0
|
||||
end
|
||||
|
||||
def self.create(user, lesson_booking, lesson_package_type, year = nil, month = nil)
|
||||
purchase = LessonPackagePurchase.new
|
||||
purchase.user = user
|
||||
|
|
@ -118,11 +122,15 @@ module JamRuby
|
|||
lesson_booking.suspend!
|
||||
end
|
||||
|
||||
subject = "Unable to charge user #{student.email} for lesson #{self.id} (stripe)"
|
||||
subject = "Unable to charge user #{student.email} for lesson #{self.id} (stripe=#{billing_error_reason})"
|
||||
body = "teacher=#{teacher.email}\n\nbilling_error_reason=#{billing_error_reason}\n\nbilling_error_detail = #{billing_error_detail}"
|
||||
AdminMailer.alerts({subject: subject, body: body})
|
||||
UserMailer.student_unable_charge_monthly(self)
|
||||
|
||||
if lesson_booking.is_suspended?
|
||||
UserMailer.teacher_unable_charge_monthly(self)
|
||||
end
|
||||
|
||||
return false
|
||||
rescue Exception => e
|
||||
|
||||
|
|
@ -139,11 +147,13 @@ module JamRuby
|
|||
# If the charge is successful, then we post the charge to the student’s payment history,
|
||||
# and associate the charge with the lesson, so that everyone knows the student has paid, and we send an email
|
||||
|
||||
UserMailer.student_lesson_normal_done(self).deliver
|
||||
UserMailer.teacher_lesson_normal_done(self).deliver
|
||||
UserMailer.student_lesson_monthly_charged(self).deliver
|
||||
UserMailer.teacher_lesson_monthly_charged(self).deliver
|
||||
|
||||
self.sent_billing_notices = true
|
||||
self.sent_billing_notices_at = Time.now
|
||||
self.post_processed = true
|
||||
self.post_processed_at = Time.now
|
||||
self.save(validate: false)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ module JamRuby
|
|||
STATUS_MISSED = 'missed'
|
||||
STATUS_COMPLETED = 'completed'
|
||||
STATUS_APPROVED = 'approved'
|
||||
STATUS_SUSPENDED = 'suspended'
|
||||
|
||||
STATUS_TYPES = [STATUS_REQUESTED, STATUS_CANCELED, STATUS_MISSED, STATUS_COMPLETED, STATUS_APPROVED]
|
||||
STATUS_TYPES = [STATUS_REQUESTED, STATUS_CANCELED, STATUS_MISSED, STATUS_COMPLETED, STATUS_APPROVED, STATUS_SUSPENDED]
|
||||
|
||||
LESSON_TYPE_SINGLE = 'paid'
|
||||
LESSON_TYPE_SINGLE_FREE = 'single-free'
|
||||
|
|
@ -52,6 +53,11 @@ module JamRuby
|
|||
|
||||
end
|
||||
|
||||
def suspend!
|
||||
self.status = STATUS_SUSPENDED
|
||||
self.save
|
||||
end
|
||||
|
||||
def self.hourly_check
|
||||
analyse_sessions
|
||||
complete_sessions
|
||||
|
|
@ -100,7 +106,6 @@ module JamRuby
|
|||
else
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def analysis_to_json(analysis)
|
||||
|
|
@ -217,6 +222,8 @@ module JamRuby
|
|||
|
||||
self.sent_billing_notices = true
|
||||
self.sent_billing_notices_at = Time.now
|
||||
self.post_processed = true
|
||||
self.post_processed_at = Time.now
|
||||
self.save(validate: false)
|
||||
end
|
||||
|
||||
|
|
@ -384,6 +391,9 @@ module JamRuby
|
|||
status == STATUS_APPROVED
|
||||
end
|
||||
|
||||
def is_suspended?
|
||||
status == STATUS_SUSPENDED
|
||||
end
|
||||
|
||||
def validate_creating
|
||||
if !is_requested? && !is_approved?
|
||||
|
|
|
|||
|
|
@ -13,6 +13,22 @@ describe LessonBooking 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]}
|
||||
|
||||
describe "suspend!" do
|
||||
it "should set status as well as update status of all associated lesson_sessions" do
|
||||
booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60)
|
||||
booking.lesson_sessions[0].accept({message: "got it", slot: booking.lesson_booking_slots[0].id})
|
||||
|
||||
booking.suspend!
|
||||
booking.errors.any?.should be false
|
||||
booking.reload
|
||||
booking.status.should eql LessonBooking::STATUS_SUSPENDED
|
||||
booking.lesson_sessions.count.should eql 2
|
||||
booking.lesson_sessions.each do |lesson_session|
|
||||
lesson_session.status.should eql LessonBooking::STATUS_SUSPENDED
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "bill_monthlies" do
|
||||
it "empty" do
|
||||
|
||||
|
|
@ -38,9 +54,17 @@ describe LessonBooking do
|
|||
now = Time.now
|
||||
billables = LessonBooking.billable_monthlies(now)
|
||||
billables.all.should eql [booking]
|
||||
LessonPackagePurchase.where(lesson_booking_id: booking.id).count.should eql 0
|
||||
# to make this billable monthly go away, we will need to create one LessonPackagePurchase; one for this month (because it's only one we have lessons in)
|
||||
# and further, mark them both as post_processed
|
||||
|
||||
package = LessonPackagePurchase.create(user, booking, LessonPackageType.single, 2016, 1)
|
||||
|
||||
LessonBooking.billable_monthlies(now).count.should eql 1
|
||||
|
||||
package.post_processed = true
|
||||
package.save!
|
||||
|
||||
LessonBooking.billable_monthlies(now).count.should eql 0
|
||||
end
|
||||
end
|
||||
|
|
@ -235,8 +259,9 @@ 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 false
|
||||
|
||||
ChatMessage.count.should eq 1
|
||||
user.reload
|
||||
user.remaining_test_drives.should eql 0
|
||||
|
||||
booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
|
||||
booking.errors.any?.should be true
|
||||
|
|
|
|||
Loading…
Reference in New Issue