-
-
diff --git a/admin/public/favicon.ico b/admin/public/favicon.ico
deleted file mode 100644
index e69de29bb..000000000
diff --git a/admin/public/robots.txt b/admin/public/robots.txt
deleted file mode 100644
index 085187fa5..000000000
--- a/admin/public/robots.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
-#
-# To ban all spiders from the entire site uncomment the next two lines:
-# User-Agent: *
-# Disallow: /
diff --git a/db/manifest b/db/manifest
index 5d4ca82e9..37a272648 100755
--- a/db/manifest
+++ b/db/manifest
@@ -376,3 +376,4 @@ jam_track_download_rights.sql
guitar_center_integration_v1.sql
mobile_recording_support.sql
youtube_broadcast.sql
+amazon_v1.sql
\ No newline at end of file
diff --git a/db/up/amazon_v1.sql b/db/up/amazon_v1.sql
new file mode 100644
index 000000000..c517d3feb
--- /dev/null
+++ b/db/up/amazon_v1.sql
@@ -0,0 +1,38 @@
+-- create new lesson package types
+
+ALTER TABLE lesson_package_types ADD COLUMN is_test_drive BOOLEAN NOT NULL DEFAULT TRUE;
+UPDATE lesson_package_types SET is_test_drive = FALSE WHERE id in('single', 'single-free');
+
+INSERT INTO lesson_package_types (id, name, description, package_type, price, is_test_drive) VALUES ('amazon-test-drive-free-4', 'Test Drive (4)', 'Four free lessons which you can use to find that ideal teacher.', 'test-drive-4', 0.0, TRUE);
+INSERT INTO lesson_package_types (id, name, description, package_type, price, is_test_drive) VALUES ('amazon-test-drive-free-2', 'Test Drive (2)', 'Two free lessons which you can use to find that ideal teacher.', 'test-drive-2', 0.0, TRUE);
+INSERT INTO lesson_package_types (id, name, description, package_type, price, is_test_drive) VALUES ('amazon-test-drive-paid-4', 'Test Drive (4)', 'Four reduced-price lessons which you can use to find that ideal teacher.', 'test-drive-4', 19.99, TRUE);
+
+
+-- need new posa card types for Amazon -- should be able to point to lesson package type
+ALTER TABLE posa_cards ADD COLUMN lesson_package_type_id VARCHAR(64) REFERENCES lesson_package_types(id) ON DELETE SET NULL;
+ALTER TABLE posa_cards ADD COLUMN credits INTEGER NOT NULL DEFAULT 1;
+ALTER TABLE posa_cards ADD COLUMN is_lesson BOOLEAN NOT NULL DEFAULT FALSE;
+ALTER TABLE posa_cards ADD COLUMN preactivate BOOLEAN NOT NULL DEFAULT FALSE;
+ALTER TABLE posa_cards ADD COLUMN requires_purchase BOOLEAN NOT NULL DEFAULT FALSE;
+ALTER TABLE posa_cards ADD COLUMN purchased BOOLEAN NOT NULL DEFAULT TRUE;
+
+ALTER TABLE lesson_bookings ADD COLUMN posa_card_purchased BOOLEAN NOT NULL DEFAULT TRUE;
+
+update posa_cards set credits = 5 where card_type = 'jam_tracks_5';
+update posa_cards set credits = 10 where card_type = 'jam_tracks_10';
+update posa_cards set credits = 4 where card_type = 'jam_class_4';
+update posa_cards set is_lesson = TRUE where card_type = 'jam_class_4';
+update posa_cards set lesson_package_type_id = 'test-drive' where card_type = 'jam_class_4';
+
+ALTER TABLE lesson_package_purchases ADD COLUMN total_roll_forward_amount_in_cents INTEGER;
+ALTER TABLE lesson_package_purchases ADD COLUMN remaining_roll_forward_amount_in_cents INTEGER;
+ALTER TABLE lesson_package_purchases ADD COLUMN reduced_roll_forward_amount_in_cents INTEGER NOT NULL DEFAULT 0;
+
+ALTER TABLE lesson_package_purchases ADD COLUMN expected_session_times INTEGER;
+ALTER TABLE lesson_package_purchases ADD COLUMN actual_session_times INTEGER;
+ALTER TABLE teacher_distributions ADD COLUMN reduced_roll_forward_amount_in_cents INTEGER NOT NULL DEFAULT 0;
+
+ALTER TABLE lesson_bookings ADD COLUMN remaining_roll_forward_amount_in_cents INTEGER NOT NULL DEFAULT 0;
+
+ALTER TABLE users ADD COLUMN lesson_package_needs_purchase_id VARCHAR(64) REFERENCES lesson_package_types(id) ON DELETE SET NULL;
+
diff --git a/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb b/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb
index 993b9f81b..79f5c2c0f 100644
--- a/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb
+++ b/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb
@@ -20,6 +20,14 @@ module JamRuby
subject: options[:subject])
end
+ def jamclass_alerts(options)
+ mail(to: APP_CONFIG.email_jamclass_alerts_alias,
+ from: APP_CONFIG.email_generic_from,
+ body: options[:body],
+ content_type: "text/plain",
+ subject: options[:subject])
+ end
+
def crash_alert(options)
mail(to: APP_CONFIG.email_crashes_alias,
from: APP_CONFIG.email_generic_from,
diff --git a/ruby/lib/jam_ruby/connection_manager.rb b/ruby/lib/jam_ruby/connection_manager.rb
index 92aea6228..a540bab41 100644
--- a/ruby/lib/jam_ruby/connection_manager.rb
+++ b/ruby/lib/jam_ruby/connection_manager.rb
@@ -355,7 +355,7 @@ SQL
if active_music_session
music_session = active_music_session.music_session
- if music_session.session_controller_id && !active_music_session.users.exists?(music_session.session_controller)
+ if music_session.session_controller_id && !active_music_session.users.exists?(music_session.session_controller.id)
# find next in line, because the current 'session controller' is not part of the session
next_in_line(music_session, active_music_session)
end
diff --git a/ruby/lib/jam_ruby/lib/guitar_center.rb b/ruby/lib/jam_ruby/lib/guitar_center.rb
index bfbd34526..a722eec7f 100644
--- a/ruby/lib/jam_ruby/lib/guitar_center.rb
+++ b/ruby/lib/jam_ruby/lib/guitar_center.rb
@@ -88,13 +88,12 @@ module JamRuby
# If this is a lesson posa card, then put that user into the guitar center school
def self.post_posa_claim(posa)
if posa.is_lesson_posa_card?
+ posa.user.is_a_student = true
# Associate user with guitar center school
- if posa.retailer.is_guitar_center?
- posa.user.is_a_student = true
+ if posa.retailer && posa.retailer.is_guitar_center?
if posa.user.school_id.nil?
posa.user.school_id = School.guitar_center.id
end
-
posa.user.save
end
end
diff --git a/ruby/lib/jam_ruby/models/lesson_booking.rb b/ruby/lib/jam_ruby/models/lesson_booking.rb
index 60572f61b..415b54a0b 100644
--- a/ruby/lib/jam_ruby/models/lesson_booking.rb
+++ b/ruby/lib/jam_ruby/models/lesson_booking.rb
@@ -9,7 +9,7 @@ module JamRuby
@@log = Logging.logger[LessonBooking]
- attr_accessor :accepting, :countering, :canceling, :autocanceling, :countered_slot, :countered_lesson, :current_purchase, :current_lesson
+ attr_accessor :accepting, :countering, :canceling, :autocanceling, :countered_slot, :countered_lesson, :current_purchase, :current_lesson, :expected_session_times, :adjustment_in_cents
STATUS_REQUESTED = 'requested'
STATUS_CANCELED = 'canceled'
@@ -97,7 +97,7 @@ module JamRuby
end
def after_create
- if (posa_card || card_presumed_ok || !payment_if_school_on_school?) && !sent_notices
+ if ((posa_card && posa_card.purchased) || card_presumed_ok || !payment_if_school_on_school?) && !sent_notices
send_notices
end
end
@@ -188,7 +188,7 @@ module JamRuby
success = self.save
if !success
- puts "unable to accept lesson booking #{errors.inspect}"
+ #puts "unable to accept lesson booking #{errors.inspect}"
end
success
end
@@ -467,6 +467,11 @@ module JamRuby
def resolved_test_drive_package
result = nil
+ # posa card is best indicator of lesson package type
+ if posa_card
+ return posa_card.lesson_package_type
+ end
+
purchase = student.most_recent_test_drive_purchase
if purchase
# for lessons already packaged
@@ -528,21 +533,22 @@ module JamRuby
end
end
- def distribution_price_in_cents(target, education, split = nil, fee_rate = nil)
- distribution = teacher_distribution_price_in_cents(target)
+ def distribution_price_in_cents(target, education, split = nil)
+
if split
+ distribution = teacher_distribution_price_in_cents(target, split)
(distribution * split).round
-
- # when a split is provided, we also pin down the teacher_fee_in_cents, instead of assuming a bunch of stuff
elsif education
+ distribution = teacher_distribution_price_in_cents(target, 0.0625)
(distribution * 0.0625).round # 0.0625 is 1/4th of 25%
else
+ distribution = teacher_distribution_price_in_cents(target)
distribution
end
end
- def teacher_distribution_price_in_cents(target)
+ def teacher_distribution_price_in_cents(target, split = nil)
if is_single_free?
0
elsif is_test_drive?
@@ -558,13 +564,76 @@ module JamRuby
# we are in the month being billed. we should set the start date based on today
start_date = today
end
- (LessonSessionMonthlyPrice.price(self, start_date) * 100).round
+ price, times = LessonSessionMonthlyPrice.price(self, start_date)
+
+ price_in_cents = (price * 100).round
+ # OK, we have a suggested price based on date, but we need to now adjust if previous lessons have been unsuccessful
+
+ adjusted_price_in_cents = LessonSessionMonthlyPrice.adjust_for_missed_lessons(self, price_in_cents, split)
+
+ self.expected_session_times = times # save for later
+ self.adjustment_in_cents = price_in_cents - adjusted_price_in_cents
+ adjusted_price_in_cents
else
booked_price * 100
end
end
end
+ # find any lesson package purchases for this lesson booking for previous months that have not had adjustments
+ # we have to base this on 'now', meaning we do not consider months until they are fully closed
+ # this does mean, because we collect up to a week in advance of a new month starting, that a student will likely not see adjustments until 2 cycles forward
+ def self.previous_needing_adjustment
+ now = Time.now.utc
+ year = now.year
+ month = now.month
+
+ if month == 1
+ previous_year = year - 1
+ previous_month = 12
+ else
+ previous_year = year
+ previous_month = month - 1
+ end
+
+ LessonPackagePurchase.where(recurring: true).where('month <= ?', previous_month).where('year <= ?', previous_year).where('actual_session_times is null').where('expected_session_times is not null')
+ .limit(500)
+ end
+
+ def self.adjust_for_missed_sessions
+
+ # Go to previous lesson_package_purchase month, and see if we need to adjust for a roll forward due to missed sessions
+ previous_purchases = LessonBooking.previous_needing_adjustment
+
+ previous_purchases.each do |previous_purchase|
+
+ # XXX other monthly code uses session start time. should we be doing that?
+ successful_lessons = LessonSession.where(lesson_booking: previous_purchase.lesson_booking).where(success: true).where('analysed_at >= ? AND analysed_at < ?', previous_purchase.beginning_of_month_at, previous_purchase.end_of_month_at)
+
+ previous_purchase.actual_session_times = successful_lessons.count
+ # find out how many actual lessons were had, and then we can adjust price of this current distribution (amount_in_cents) accordingly
+
+ ratio = previous_purchase.actual_session_times.to_f / previous_purchase.expected_session_times.to_f
+
+ if ratio < 1
+ # discount next month for student
+ amount_paid_last_month_in_cents = previous_purchase.price_in_cents # this does not include tax. It's just the expected price of the booking
+ previous_purchase.remaining_roll_forward_amount_in_cents = previous_purchase.total_roll_forward_amount_in_cents = (amount_paid_last_month_in_cents * ratio).round
+
+ # if there is a roll forward, add it to the lesson booking
+ previous_purchase.lesson_booking.remaining_roll_forward_amount_in_cents += previous_purchase.remaining_roll_forward_amount_in_cents
+ previous_purchase.lesson_booking.save!
+
+ else
+ previous_purchase.total_roll_forward_amount_in_cents = 0
+ previous_purchase.applied_roll_forward_amount_in_cents = 0
+ end
+
+ previous_purchase.save
+ end
+ end
+
+
def is_single_free?
lesson_type == LESSON_TYPE_FREE
end
@@ -674,6 +743,9 @@ module JamRuby
def card_approved
self.card_presumed_ok = true
+ if posa_card_id
+ self.posa_card_purchased = true
+ end
if self.save && !sent_notices
send_notices
end
@@ -792,7 +864,10 @@ module JamRuby
if lesson_type == LESSON_TYPE_TEST_DRIVE
# if the user has any jamclass credits, then we should get their most recent posa purchase
if user.jamclass_credits > 0
- lesson_booking.posa_card = user.most_recent_posa_purchase.posa_card
+ lesson_booking.posa_card = user.most_recent_posa_card
+ if lesson_booking.posa_card
+ lesson_booking.posa_card_purchased = lesson_booking.posa_card.purchased
+ end
else
# otherwise, it's a normal test drive, and we should honor test_drive_package_choice if specified
lesson_booking.test_drive_package_choice = test_drive_package_choice
@@ -837,7 +912,7 @@ module JamRuby
end
def self.unprocessed(current_user)
- LessonBooking.where(user_id: current_user.id).where(card_presumed_ok: false).where(same_school_free: false).where(posa_card:nil)
+ LessonBooking.where(user_id: current_user.id).where(card_presumed_ok: false).where(same_school_free: false).where('posa_card_id is null OR (posa_card_id is not null AND posa_card_purchased = false)')
end
def self.requested(current_user)
@@ -902,9 +977,15 @@ module JamRuby
# check for any recurring sessions where there are not at least 2 sessions into the future. If not, we need to make sure they get made
def self.hourly_check
schedule_upcoming_lessons
+
+ # order matters: bill_monthly code will use the adjustments made in here to correct billing roll forward
+ adjust_for_missed_sessions
+
+ # needs to come after 'adjust_for_missed_sessions'
bill_monthlies
end
+
def self.bill_monthlies
now = Time.now
billable_monthlies(now).each do |lesson_booking|
diff --git a/ruby/lib/jam_ruby/models/lesson_package_purchase.rb b/ruby/lib/jam_ruby/models/lesson_package_purchase.rb
index 384358422..481e108cd 100644
--- a/ruby/lib/jam_ruby/models/lesson_package_purchase.rb
+++ b/ruby/lib/jam_ruby/models/lesson_package_purchase.rb
@@ -29,14 +29,24 @@ module JamRuby
after_create :add_test_drives
after_create :create_charge
+
+
+
def validate_test_drive
if user
# if this is a posa card purchase, we won't stop it from getting created
- if posa_card_id
+ if posa_card
return
end
- if lesson_package_type.is_test_drive? && !user.can_buy_test_drive?
- errors.add(:user, "can not buy test drive right now because you have already purchased it within the last year")
+
+ if lesson_package_type.is_test_drive?
+ if user.lesson_package_needs_purchase_id
+ # if lesson_package_needs_purchase is set, we need to let the purchase go through because the user alrady has the credits; gotta let them pay
+ return
+ end
+ if !user.can_buy_test_drive?
+ errors.add(:user, "can not buy test drive right now because you have already purchased it within the last year")
+ end
end
end
end
@@ -61,7 +71,7 @@ module JamRuby
end
def add_test_drives
- if posa_card_id
+ if posa_card
#user.jamclass_credits incremented in posa_card.rb
return
end
@@ -85,12 +95,28 @@ module JamRuby
lesson_payment_charge.amount_in_cents / 100.0
end
+ def beginning_of_month_at
+ Date.new(year, month, 1).to_time.utc.beginning_of_month
+ end
+
+ def end_of_month_at
+ Date.new(year, month, 1).to_time.utc.end_of_month
+ end
+
+ def period_over?
+ Time.now.utc > end_of_month_at
+ end
+
def self.create(user, lesson_booking, lesson_package_type, year = nil, month = nil, posa_card = nil)
purchase = LessonPackagePurchase.new
purchase.user = user
purchase.lesson_booking = lesson_booking
purchase.teacher = lesson_booking.teacher if lesson_booking
purchase.posa_card = posa_card
+ if !purchase.posa_card && lesson_booking
+ # the lesson booking has a posa card? if so, we should track that
+ purchase.posa_card = lesson_booking.posa_card
+ end
if year
purchase.year = year
@@ -117,6 +143,7 @@ module JamRuby
purchase.teacher_distributions << teacher_dist
# price should always match the teacher_distribution, if there is one
purchase.price = teacher_dist.amount_in_cents / 100
+ purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
end
if retailer_split && retailer_split > 0
@@ -124,33 +151,39 @@ module JamRuby
teacher_dist.retailer = teacher.teacher.retailer
teacher_dist.save
purchase.teacher_distributions << teacher_dist
+ purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
end
else
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, false)
purchase.teacher_distributions << teacher_dist
+ purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
# price should always match the teacher_distribution, if there is one
purchase.price = teacher_dist.amount_in_cents / 100
if lesson_booking.school_on_school_payment? && lesson_booking.school.education
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, true)
purchase.teacher_distributions << teacher_dist
+ purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
end
end
+
+ # record expected times for times played in the month
+ purchase.expected_session_times = lesson_booking.expected_session_times
end
else
purchase.recurring = false
end
if lesson_booking
+
purchase.lesson_package_type = lesson_package_type ? lesson_package_type : lesson_booking.lesson_package_type
purchase.price = lesson_booking.booked_price if purchase.price.nil?
else
purchase.lesson_package_type = lesson_package_type
purchase.price = lesson_package_type.price if purchase.price.nil?
end
-
purchase.save
purchase
end
diff --git a/ruby/lib/jam_ruby/models/lesson_package_type.rb b/ruby/lib/jam_ruby/models/lesson_package_type.rb
index 5424763fd..7c93e3d98 100644
--- a/ruby/lib/jam_ruby/models/lesson_package_type.rb
+++ b/ruby/lib/jam_ruby/models/lesson_package_type.rb
@@ -6,11 +6,16 @@ module JamRuby
PRODUCT_TYPE = 'LessonPackageType'
+ TEST_DRIVE_4_ID = 'test-drive'
+
SINGLE_FREE = 'single-free'
- TEST_DRIVE_4 = 'test-drive'
+ TEST_DRIVE_4 = 'test-drive-4'
TEST_DRIVE_2 = 'test-drive-2'
TEST_DRIVE_1 = 'test-drive-1'
SINGLE = 'single'
+ AMAZON_TEST_DRIVE_4_PAID_ID = 'amazon-test-drive-paid-4'
+ AMAZON_TEST_DRIVE_FREE_2_ID = 'amazon-test-drive-free-2'
+ AMAZON_TEST_DRIVE_FREE_4_ID = 'amazon-test-drive-free-4'
LESSON_PACKAGE_TYPES =
[
@@ -22,14 +27,18 @@ module JamRuby
]
has_many :user_desired_packages, class_name: "JamRuby::User", :foreign_key => "lesson_package_type_id", inverse_of: :desired_package
+ has_many :users_needing_purchase, class_name: "JamRuby::User", :foreign_key => "lesson_package_needs_purchase_id", inverse_of: :lesson_package_needs_purchase
+ has_many :posa_cards, class_name: "JamRuby::PosaCard"
+
validates :name, presence: true
validates :description, presence: true
validates :price, presence: true
validates :package_type, presence: true, inclusion: {in: LESSON_PACKAGE_TYPES}
def self.test_drive_package_ids
- [TEST_DRIVE_4, TEST_DRIVE_2, TEST_DRIVE_1]
+ LessonPackageType.select("id").where(is_test_drive: true).map {|i| i.id }
end
+
def self.monthly
LessonPackageType.find(MONTHLY)
end
@@ -38,8 +47,20 @@ module JamRuby
LessonPackageType.find(SINGLE_FREE)
end
+ def self.amazon_test_drive_free_4
+ LessonPackageType.find(AMAZON_TEST_DRIVE_FREE_4_ID)
+ end
+
+ def self.amazon_test_drive_free_2
+ LessonPackageType.find(AMAZON_TEST_DRIVE_FREE_2_ID)
+ end
+
+ def self.amazon_test_drive_paid_4
+ LessonPackageType.find(AMAZON_TEST_DRIVE_4_PAID_ID)
+ end
+
def self.test_drive_4
- LessonPackageType.find(TEST_DRIVE_4)
+ LessonPackageType.find(TEST_DRIVE_4_ID)
end
def self.test_drive_2
@@ -54,6 +75,7 @@ module JamRuby
LessonPackageType.find(SINGLE)
end
+
def booked_price(lesson_booking)
if is_single_free?
0
@@ -94,7 +116,7 @@ module JamRuby
end
def is_test_drive?
- id.start_with?('test-drive')
+ is_test_drive
end
def is_normal?
@@ -112,7 +134,7 @@ module JamRuby
def plan_code
if package_type == SINGLE_FREE
"lesson-package-single-free"
- elsif package_type == 'test-drive-4'
+ elsif package_type == TEST_DRIVE_4
"lesson-package-test-drive-4"
elsif package_type == TEST_DRIVE_2
"lesson-package-test-drive-2"
diff --git a/ruby/lib/jam_ruby/models/lesson_session.rb b/ruby/lib/jam_ruby/models/lesson_session.rb
index dc080d4a5..55fa41c6e 100644
--- a/ruby/lib/jam_ruby/models/lesson_session.rb
+++ b/ruby/lib/jam_ruby/models/lesson_session.rb
@@ -11,7 +11,7 @@ module JamRuby
@@log = Logging.logger[LessonSession]
delegate :sent_billing_notices, :last_billing_attempt_at, :billing_attempts, :billing_should_retry, :billed_at, :billing_error_detail, :billing_error_reason, :is_card_declined?, :is_card_expired?, :last_billed_at_date, :sent_billing_notices, to: :lesson_payment_charge, allow_nil: true
- delegate :is_test_drive?, :is_single_free?, :is_normal?, :approved_before?, :is_active?, :recurring, :is_monthly_payment?, :school_on_school?, :school_on_school_payment?, :no_school_on_school_payment?, :payment_if_school_on_school?, :scheduling_email, :teacher_school_emails, :school_and_teacher, :school_over_teacher, :school_and_teacher_ids, :school_over_teacher_ids, :posa_card, to: :lesson_booking
+ delegate :is_test_drive?, :is_single_free?, :is_normal?, :approved_before?, :is_active?, :recurring, :is_monthly_payment?, :school_on_school?, :school_on_school_payment?, :no_school_on_school_payment?, :payment_if_school_on_school?, :scheduling_email, :teacher_school_emails, :school_and_teacher, :school_over_teacher, :school_and_teacher_ids, :school_over_teacher_ids, :posa_card, :remaining_roll_forward_amount_in_cents, to: :lesson_booking
delegate :pretty_scheduled_start, to: :music_session
@@ -712,7 +712,7 @@ module JamRuby
end
# only show 'fully booked lessons'; not those they can not possibly be paid for
- query = query.where('lesson_bookings.posa_card_id IS NOT NULL OR lesson_bookings.card_presumed_ok = true OR (music_sessions.user_id = ?) ' + school_extra, user.id)
+ query = query.where('(lesson_bookings.posa_card_id IS NOT NULL AND lesson_bookings.posa_card_purchased) OR lesson_bookings.card_presumed_ok = true OR (music_sessions.user_id = ?) ' + school_extra, user.id)
current_page = params[:page].nil? ? 1 : params[:page].to_i
next_page = current_page + 1
diff --git a/ruby/lib/jam_ruby/models/lesson_session_monthly_price.rb b/ruby/lib/jam_ruby/models/lesson_session_monthly_price.rb
index b2eae100c..b90345d21 100644
--- a/ruby/lib/jam_ruby/models/lesson_session_monthly_price.rb
+++ b/ruby/lib/jam_ruby/models/lesson_session_monthly_price.rb
@@ -6,6 +6,7 @@ module JamRuby
raise "lesson_booking is not monthly paid #{lesson_booking.admin_url}" if !lesson_booking.is_monthly_payment?
+
data = lesson_booking.predicted_times_for_month(start_day.year, start_day.month)
times = data[:times]
@@ -33,7 +34,53 @@ module JamRuby
result = lesson_booking.booked_price
end
- result
+ [result, times.length]
+ end
+
+ def self.adjust_for_missed_lessons(lesson_booking, price_in_cents, split)
+
+ if !split
+ split = 1.0
+ end
+
+ adjusted_price_in_cents = price_in_cents
+
+ lesson_package_purchases = LessonPackagePurchase.where(lesson_booking: lesson_booking).where('remaining_roll_forward_amount_in_cents > 0').order(:created_at)
+ remaining_roll_forward = 0
+ skip_crediting = false
+ reduced_amount = 0
+
+ lesson_package_purchases.each do |lesson_package_purchase|
+
+ if !skip_crediting
+
+ take_off = (lesson_package_purchase.remaining_roll_forward_amount_in_cents * split).round
+
+ amount_remaining_after_adjustment = price_in_cents - take_off
+
+ if amount_remaining_after_adjustment <= 0
+ # there isn't enough 'price_in_cents' to eat up the needed credit, so we say this teacher_distribution has no due price, and break out of the loo
+ adjusted_price_in_cents = 0
+ reduced_amount += price_in_cents
+ lesson_package_purchase.remaining_roll_forward_amount_in_cents -= price_in_cents
+ skip_crediting = true
+ else
+ adjusted_price_in_cents = amount_remaining_after_adjustment
+ reduced_amount += take_off
+ # this lesson_package_purchase is now cleared out - totally credited!
+ lesson_package_purchase.remaining_roll_forward_amount_in_cents = 0
+ end
+ end
+
+ remaining_roll_forward = remaining_roll_forward + lesson_package_purchase.remaining_roll_forward_amount_in_cents
+
+ lesson_package_purchase.save!
+ end
+
+ lesson_booking.remaining_roll_forward_amount_in_cents = remaining_roll_forward
+ lesson_booking.save!
+
+ adjusted_price_in_cents
end
end
end
diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb
index a36c41987..4f965a4a6 100644
--- a/ruby/lib/jam_ruby/models/music_session.rb
+++ b/ruby/lib/jam_ruby/models/music_session.rb
@@ -126,7 +126,6 @@ module JamRuby
def refresh_stream(user, broadcast)
stream_data = get_livestream(user)
- puts "REFRESH STREAM #{stream_data}"
broadcast.stream_id = stream_data["id"]
broadcast.stream_status = stream_data["status"]["streamStatus"]
broadcast.stream_name = stream_data["cdn"]["ingestionInfo"]["streamName"]
@@ -185,7 +184,6 @@ module JamRuby
if livestream
# if livestream["status"]["streamStatus"] == "active"
- puts "LI EVESATREMA STATATUESNUHOENTUHENOSTHU #{livestream["status"]["streamStatus"]}"
transition_broadcast(user, broadcast, 'live', google_client)
# end
else
@@ -1254,6 +1252,10 @@ SQL
sessions
end
+ def admin_url
+ APP_CONFIG.admin_root_url + "/admin/music_sessions/" + id
+ end
+
private
def generate_share_token
diff --git a/ruby/lib/jam_ruby/models/posa_card.rb b/ruby/lib/jam_ruby/models/posa_card.rb
index 68fe8d11a..604dee8fe 100644
--- a/ruby/lib/jam_ruby/models/posa_card.rb
+++ b/ruby/lib/jam_ruby/models/posa_card.rb
@@ -7,20 +7,25 @@ module JamRuby
JAM_TRACKS_5 = 'jam_tracks_5'
JAM_TRACKS_10 = 'jam_tracks_10'
JAM_CLASS_4 = 'jam_class_4'
+ JAM_CLASS_2 = 'jam_class_2'
CARD_TYPES =
[
JAM_TRACKS_5,
JAM_TRACKS_10,
- JAM_CLASS_4
+ JAM_CLASS_4,
+ JAM_CLASS_2
]
belongs_to :user, class_name: "JamRuby::User"
belongs_to :retailer, class_name: "JamRuby::Retailer"
+ belongs_to :lesson_package_type, class_name: "JamRuby::LessonPackageType"
+
has_many :posa_card_purchases, class_name: 'JamRuby::PosaCardPurchase'
has_one :lesson_package_purchase, class_name: 'JamRuby::LessonPackagePurchase'
has_one :jam_track_right, class_name: "JamRuby::JamTrackRight"
+
validates :card_type, presence: true, inclusion: {in: CARD_TYPES}
validates :code, presence: true, uniqueness: true
@@ -34,20 +39,9 @@ module JamRuby
validate :within_one_year
def is_lesson_posa_card?
- card_type == JAM_CLASS_4
+ self.is_lesson
end
- def credits
- if card_type == JAM_TRACKS_5
- 5
- elsif card_type == JAM_TRACKS_10
- 10
- elsif card_type == JAM_CLASS_4
- 4
- else
- raise "unknown card type #{card_type}"
- end
- end
def already_activated
if activated_at && activated_at_was && activated_at_changed?
@@ -60,7 +54,7 @@ module JamRuby
end
def within_one_year
- if user && claimed_at && claimed_at_was && claimed_at_changed?
+ if user && claimed_at && !claimed_at_was && claimed_at_changed?
if !user.can_claim_posa_card
self.errors.add(:claimed_at, 'was within 1 year')
end
@@ -86,7 +80,7 @@ module JamRuby
end
def must_be_activated
- if claimed_at && !activated_at
+ if claimed_at && !preactivate && !activated_at
self.errors.add(:activated_at, 'must already be set')
end
end
@@ -94,11 +88,11 @@ module JamRuby
def check_attributed
if user && user_id_changed?
if card_type == JAM_TRACKS_5
- user.gifted_jamtracks += 5
+ user.gifted_jamtracks += credits
elsif card_type == JAM_TRACKS_10
- user.gifted_jamtracks += 10
- elsif card_type == JAM_CLASS_4
- user.jamclass_credits += 4
+ user.gifted_jamtracks += credits
+ elsif is_lesson_posa_card?
+ user.jamclass_credits += credits
else
raise "unknown card type #{card_type}"
end
@@ -106,18 +100,6 @@ module JamRuby
end
end
- def lesson_package_type
- if card_type == JAM_TRACKS_5
- raise 'not a lesson package: ' + card_type
- elsif card_type == JAM_TRACKS_10
- raise 'not a lesson package: ' + card_type
- elsif card_type == JAM_CLASS_4
- LessonPackageType.test_drive_4
- else
- raise "unknown card type #{card_type}"
- end
- end
-
def product_info
price = nil
plan_code = nil
@@ -128,9 +110,9 @@ module JamRuby
elsif card_type == JAM_TRACKS_10
price = 19.99
plan_code = 'posa-jatracks-10'
- elsif card_type == JAM_CLASS_4
- price = 49.99
- plan_code = 'posa-jamclass-4'
+ elsif is_lesson_posa_card?
+ price = lesson_package_type.price
+ plan_code = "posa-jamclass-#{credits}"
else
raise "unknown card type #{card_type}"
end
@@ -148,20 +130,53 @@ module JamRuby
self.save
end
+ def has_been_purchased(create_purchase)
+ if !purchased
+
+ #release flag on user account indicating they need to buy this card
+ user.lesson_package_needs_purchase = nil
+ user.save
+
+ # indicate this has been purchased
+ self.purchased = true
+ self.save
+
+ if is_lesson_posa_card? && create_purchase
+ LessonPackagePurchase.create(user, nil, lesson_package_type, nil, nil, self)
+ end
+ end
+ end
+
def claim(user)
self.user = user
self.claimed_at = Time.now
if self.save
+ if user.errors.any?
+ # happens on signup if bad email etc
+ return
+ end
+
UserWhitelist.card_create(user, 'posa')
- SaleLineItem.associate_user_for_posa(self, user)
+
+ if !preactivate
+ SaleLineItem.associate_user_for_posa(self, user)
+ end
+
+ if requires_purchase
+ # this is a flag on the account that makes it so the user can't buy any TD's on the payment page until they clear this up, because we've given them credits
+ user.lesson_package_needs_purchase = self.lesson_package_type
+ user.save
+ end
# when you claim a POSA card, you are also making a LessonPackagePurchase
if is_lesson_posa_card?
GuitarCenter.post_posa_claim(self)
- purchase = LessonPackagePurchase.create(user, nil, lesson_package_type, nil, nil, self) if purchase.nil?
+ if purchased
+ LessonPackagePurchase.create(user, nil, lesson_package_type, nil, nil, self)
+ end
end
end
end
@@ -171,8 +186,8 @@ module JamRuby
'JT-5'
elsif card_type == JAM_TRACKS_10
'JT-10'
- elsif card_type == JAM_CLASS_4
- 'JC-4'
+ elsif is_lesson_posa_card?
+ "JC-#{credits}"
else
raise "unknown card type #{card_type}"
end
diff --git a/ruby/lib/jam_ruby/models/sale.rb b/ruby/lib/jam_ruby/models/sale.rb
index 97a633e90..c71726434 100644
--- a/ruby/lib/jam_ruby/models/sale.rb
+++ b/ruby/lib/jam_ruby/models/sale.rb
@@ -233,8 +233,8 @@ module JamRuby
free && non_free
end
- def self.purchase_test_drive(current_user, lesson_package_type, booking = nil)
- self.purchase_lesson(nil, current_user, booking, lesson_package_type)
+ def self.purchase_test_drive(current_user, lesson_package_type, booking = nil, posa_card = nil)
+ self.purchase_lesson(nil, current_user, booking, lesson_package_type, nil, nil, false, posa_card)
end
def self.post_sale_test_failure
@@ -261,7 +261,7 @@ module JamRuby
end
# this is easy to make generic, but right now, it just purchases lessons
- def self.purchase_lesson(charge, current_user, lesson_booking, lesson_package_type, lesson_session = nil, lesson_package_purchase = nil, force = false)
+ def self.purchase_lesson(charge, current_user, lesson_booking, lesson_package_type, lesson_session = nil, lesson_package_purchase = nil, force = false, posa_card = nil)
stripe_charge = nil
sale = nil
purchase = nil
@@ -279,7 +279,7 @@ module JamRuby
sale_line_item = SaleLineItem.create_from_lesson_package(current_user, sale, lesson_package_type, lesson_booking)
- price_info = charge_stripe_for_lesson(charge, current_user, lesson_booking, lesson_package_type, sale_line_item, lesson_session, lesson_package_purchase, force)
+ price_info = charge_stripe_for_lesson(charge, current_user, lesson_booking, lesson_package_type, sale_line_item, lesson_session, lesson_package_purchase, force, posa_card)
post_sale_test_failure
@@ -310,7 +310,7 @@ module JamRuby
{sale: sale, stripe_charge: stripe_charge, purchase: purchase}
end
- def self.charge_stripe_for_lesson(charge, current_user, lesson_booking, lesson_package_type, sale_line_item, lesson_session = nil, lesson_package_purchase = nil, force = false)
+ def self.charge_stripe_for_lesson(charge, current_user, lesson_booking, lesson_package_type, sale_line_item, lesson_session = nil, lesson_package_purchase = nil, force = false, posa_card = nil)
if lesson_package_purchase
target = lesson_package_purchase
elsif lesson_session
@@ -322,10 +322,9 @@ module JamRuby
current_user.sync_stripe_customer
purchase = lesson_package_purchase
- purchase = LessonPackagePurchase.create(current_user, lesson_booking, lesson_package_type) if purchase.nil?
+ purchase = LessonPackagePurchase.create(current_user, lesson_booking, lesson_package_type, nil, nil, posa_card) if purchase.nil?
if purchase.errors.any?
- puts "purchase errors #{purchase.errors.inspect}"
price_info = {}
price_info[:purchase] = purchase
return price_info
diff --git a/ruby/lib/jam_ruby/models/teacher.rb b/ruby/lib/jam_ruby/models/teacher.rb
index 20eae0864..420311e56 100644
--- a/ruby/lib/jam_ruby/models/teacher.rb
+++ b/ruby/lib/jam_ruby/models/teacher.rb
@@ -141,7 +141,7 @@ module JamRuby
query = query.where("teaches_age_lower <= ? AND (CASE WHEN teaches_age_upper = 0 THEN true ELSE teaches_age_upper >= ? END)", student_age, student_age)
end
- # don't show phantom teachers that teach 'bass guitar', 'acoustic guitar', 'electric_guitar'
+ # don't show phantom teachers that teach 'bass guitar', 'acoustic guitar', 'electric guitar'
query = query.where("((select count(checkgt.instrument_id) from teachers_instruments checkgt where checkgt.teacher_id = teachers.id AND checkgt.instrument_id IN ('bass guitar', 'acoustic guitar', 'electric guitar') ) = 0 AND phantom = true) OR phantom = false")
# order in this way: https://jamkazam.atlassian.net/browse/VRFS-4058
@@ -349,8 +349,8 @@ module JamRuby
reviews.order('created_at desc').limit(20)
end
- def mark_background_checked
- self.background_check_at = Time.now
+ def mark_background_checked(time)
+ self.background_check_at = time
self.save!
end
diff --git a/ruby/lib/jam_ruby/models/teacher_distribution.rb b/ruby/lib/jam_ruby/models/teacher_distribution.rb
index 0ad3e9856..4c99bca3e 100644
--- a/ruby/lib/jam_ruby/models/teacher_distribution.rb
+++ b/ruby/lib/jam_ruby/models/teacher_distribution.rb
@@ -55,14 +55,14 @@ module JamRuby
end
def self.create_for_lesson_package_purchase(lesson_package_purchase, for_education, split = nil, fee_rate = nil)
+
distribution = create(lesson_package_purchase, for_education, split, fee_rate)
distribution.lesson_package_purchase = lesson_package_purchase
distribution.education = for_education
-
# lock down the teacher_fee_in_cents
distribution.teacher_fee_in_cents = distribution.calculate_teacher_fee(split, fee_rate)
-
+ distribution.reduced_roll_forward_amount_in_cents = lesson_package_purchase.lesson_booking.adjustment_in_cents
distribution
end
@@ -71,7 +71,7 @@ module JamRuby
distribution.teacher = target.teacher
distribution.ready = false
distribution.distributed = false
- distribution.amount_in_cents = target.lesson_booking.distribution_price_in_cents(target, education, split, fee_rate)
+ distribution.amount_in_cents = target.lesson_booking.distribution_price_in_cents(target, education, split)
distribution.school = target.lesson_booking.school
distribution
end
diff --git a/ruby/lib/jam_ruby/models/teacher_payment.rb b/ruby/lib/jam_ruby/models/teacher_payment.rb
index 592ba99b7..66ae5222d 100644
--- a/ruby/lib/jam_ruby/models/teacher_payment.rb
+++ b/ruby/lib/jam_ruby/models/teacher_payment.rb
@@ -37,7 +37,7 @@ module JamRuby
pending_teacher_payments.each do |row|
teacher = User.find(row['id'])
- TeacherDistribution.where(teacher_id: teacher.id).where(ready:true).where(distributed: false).each do |distribution|
+ TeacherDistribution.where(teacher_id: teacher.id).where(ready: true).where(distributed: false).each do |distribution|
payment = TeacherPayment.charge(teacher)
if payment.nil? || !payment.teacher_payment_charge.billed
break
@@ -62,6 +62,7 @@ module JamRuby
def last_billed_at_date
teacher_payment_charge.last_billed_at_date
end
+
def charge_retry_hours
24
end
@@ -82,7 +83,7 @@ module JamRuby
end
if payment.teacher_distribution.nil?
- teacher_distribution = TeacherDistribution.where(teacher_id: teacher.id).where(ready:true).where(distributed: false).order(:created_at).first
+ teacher_distribution = TeacherDistribution.where(teacher_id: teacher.id).where(ready: true).where(distributed: false).order(:created_at).first
if teacher_distribution.nil?
return
end
@@ -90,34 +91,45 @@ module JamRuby
end
payment.school = payment.teacher_distribution.school
- payment.amount_in_cents = payment.teacher_distribution.amount_in_cents
+ payment.amount_in_cents = payment.teacher_distribution.amount_in_cents
payment.fee_in_cents = payment.teacher_distribution.calculate_teacher_fee
- effective_in_cents = payment.real_distribution_in_cents
+ if payment.teacher_distribution.amount_in_cents > 0
- if payment.teacher_payment_charge.nil?
- charge = TeacherPaymentCharge.new
- charge.user = payment.payable_teacher
- charge.amount_in_cents = (effective_in_cents / (1 - APP_CONFIG.stripe[:ach_pct])).round
- charge.fee_in_cents = payment.fee_in_cents
- charge.teacher_payment = payment
- payment.teacher_payment_charge = charge
- # charge.save!
+ effective_in_cents = payment.real_distribution_in_cents
+
+ if payment.teacher_payment_charge.nil?
+ charge = TeacherPaymentCharge.new
+ charge.user = payment.payable_teacher
+ charge.amount_in_cents = (effective_in_cents / (1 - APP_CONFIG.stripe[:ach_pct])).round
+ charge.fee_in_cents = payment.fee_in_cents
+ charge.teacher_payment = payment
+ payment.teacher_payment_charge = charge
+ # charge.save!
+ else
+ charge = payment.teacher_payment_charge
+ charge.amount_in_cents = (effective_in_cents / (1 - APP_CONFIG.stripe[:ach_pct])).round
+ charge.fee_in_cents = payment.fee_in_cents
+ charge.save!
+ end
+
+ payment.save!
+
+ payment.teacher_payment_charge.charge
+
+ if payment.teacher_payment_charge.billed
+ payment.teacher_distribution.distributed = true
+ payment.teacher_distribution.save!
+ end
else
- charge = payment.teacher_payment_charge
- charge.amount_in_cents = (effective_in_cents / (1 - APP_CONFIG.stripe[:ach_pct])).round
- charge.fee_in_cents = payment.fee_in_cents
- charge.save!
- end
- payment.save!
-
- payment.teacher_payment_charge.charge
-
- if payment.teacher_payment_charge.billed
+ # 0 amount distribution; these occur in 100% roll forward scenarios (previous month was completely missed)
+ payment.save!
payment.teacher_distribution.distributed = true
payment.teacher_distribution.save!
+
end
+
payment
end
end
diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb
index 55c328325..6db0617d0 100644
--- a/ruby/lib/jam_ruby/models/user.rb
+++ b/ruby/lib/jam_ruby/models/user.rb
@@ -41,7 +41,7 @@ module JamRuby
attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :birth_date, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_large_fpfile, :cropped_s3_path, :cropped_large_s3_path, :photo_url, :large_photo_url, :crop_selection
# updating_password corresponds to a lost_password
- attr_accessor :test_drive_packaging, :validate_instruments, :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field, :mods_json, :expecting_gift_card
+ attr_accessor :test_drive_packaging, :validate_instruments, :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field, :mods_json, :expecting_gift_card, :purchase_required
belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id'
has_many :controlled_sessions, :class_name => "JamRuby::MusicSession", inverse_of: :session_controller, foreign_key: :session_controller_id
@@ -82,7 +82,7 @@ module JamRuby
has_many :playing_claimed_recordings, :class_name => "JamRuby::ActiveMusicSession", :inverse_of => :claimed_recording_initiator
has_many :playing_jam_tracks, :class_name => "JamRuby::ActiveMusicSession", :inverse_of => :jam_track_initiator
- # VRFS-2916 jam_tracks.id is varchar: REMOVE
+ # VRFS-2916 jam_tracks.id is varchar: REMOVE
# has_many :jam_tracks_played, :class_name => "JamRuby::PlayablePlay", :foreign_key => 'player_id', :conditions => "jam_track_id IS NOT NULL"
# VRFS-2916 jam_tracks.id is varchar: ADD
has_many :jam_tracks_played, -> { where("playable_type = 'JamRuby::JamTrack'") }, :class_name => "JamRuby::PlayablePlay", :foreign_key => 'player_id'
@@ -178,7 +178,7 @@ module JamRuby
has_many :test_drive_package_choice_teachers, :class_name => "JamRuby::TestDrivePackageChoiceTeacher", :foreign_key => "teacher_id"
has_many :test_drive_package_choices, :class_name => "JamRuby::TestDrivePackageChoice", :foreign_key => "user_id", inverse_of: :user
belongs_to :desired_package, :class_name => "JamRuby::LessonPackageType", :foreign_key => "lesson_package_type_id", inverse_of: :user_desired_packages # used to hold whether user last wanted test drive 4/2/1
-
+ belongs_to :lesson_package_needs_purchase, :class_name => "JamRuby::LessonPackageType", :foreign_key => "lesson_package_needs_purchase_id", inverse_of: :users_needing_purchase
# Shopping carts
has_many :shopping_carts, :class_name => "JamRuby::ShoppingCart"
@@ -477,7 +477,7 @@ module JamRuby
def age
now = Time.now.utc.to_date
- self.birth_date.nil? ? "" : now.year - self.birth_date.year - (self.birth_date.to_date.change(:year => now.year) > now ? 1 : 0)
+ self.birth_date.nil? ? nil : now.year - self.birth_date.year - (self.birth_date.to_date.change(:year => now.year) > now ? 1 : 0)
end
def session_count
@@ -685,6 +685,8 @@ module JamRuby
id
end
+
+
def set_password(old_password, new_password, new_password_confirmation)
# so that UserObserver knows to send a confirmation email on success
@@ -720,7 +722,7 @@ module JamRuby
end
def change_password(new_password, new_password_confirmation)
- # FIXME: Should verify that the new password meets certain quality criteria. Really, maybe that should be a
+ # FIXME: Should verify that the new password meets certain quality criteria. Really, maybe that should be a
# verification step.
self.updating_password = true
self.password = new_password
@@ -1358,17 +1360,21 @@ module JamRuby
# if a gift card value was passed in, then try to find that gift card and apply it to user
if gift_card
-
# first try posa card
posa_card = PosaCard.where(code: gift_card).first
if posa_card
posa_card.claim(user)
user.posa_cards << posa_card
+ user.purchase_required = posa_card.requires_purchase # temporary; just so the signup page knows to send them to payment place
else
+
user.expecting_gift_card = true
found_gift_card = GiftCard.where(code: gift_card).where(user_id: nil).first
- user.gift_cards << found_gift_card if found_gift_card
+ if found_gift_card
+ user.gift_cards << found_gift_card
+ end
+
end
end
@@ -1448,6 +1454,17 @@ module JamRuby
#if school && school.education
# UserMailer.student_education_welcome_message(user).deliver_now
#else
+ body = "Name: #{user.name}\n"
+ body << "Email: #{user.email}\n"
+ body << "Admin: #{user.admin_student_url}\n"
+ if posa_card
+ body << "Package Details: \n"
+ body << " Package: #{posa_card.lesson_package_type.id}\n"
+ body << " Credits: #{posa_card.credits}\n"
+ body << " Code: #{posa_card.code}\n"
+ end
+
+ AdminMailer.jamclass_alerts({subject: "#{user.name} just signed up as a student", body: body}).deliver_now
UserMailer.student_welcome_message(user).deliver_now
#end
elsif user.is_a_teacher
@@ -1975,6 +1992,10 @@ module JamRuby
APP_CONFIG.admin_root_url + "/admin/users/" + id
end
+ def admin_student_url
+ APP_CONFIG.admin_root_url + "/admin/students" # should add id; not yet supported
+ end
+
def jam_track_rights_admin_url
APP_CONFIG.admin_root_url + "/admin/jam_track_rights?q[user_id_equals]=#{id}&commit=Filter&order=created_at DESC"
end
@@ -2042,7 +2063,7 @@ module JamRuby
# validate if within waiting period
def can_claim_posa_card
- posa_cards.where('card_type = ?', PosaCard::JAM_CLASS_4).where('claimed_at > ?', APP_CONFIG.jam_class_card_wait_period_year.years.ago).count == 0
+ posa_cards.where('is_lesson = ?', true).where('claimed_at > ?', APP_CONFIG.jam_class_card_wait_period_year.years.ago).count == 0
end
def lessons_with_teacher(teacher)
@@ -2197,6 +2218,7 @@ module JamRuby
lesson_package_type = nil
uncollectables = nil
choice = nil
+ posa_card = nil
User.transaction do
if params[:name].present?
@@ -2209,20 +2231,29 @@ module JamRuby
if params[:test_drive]
self.reload
if booking
+ # bookin will indicate test package
lesson_package_type = booking.resolved_test_drive_package
+ posa_card = booking.posa_card
elsif choice
+ # packages also indicate lesson package
lesson_package_type = choice.lesson_package_type
+ elsif self.lesson_package_needs_purchase
+ # user has a POSA card requiring purchase, so this takes preference over the 'desired_package' (a user could have both set, but we force user to pay for POSA_CARD requiring purchase before picking up a random TD purchase)
+ lesson_package_type = self.lesson_package_needs_purchase
+ # also update POSA cards indicating they have been bought. This below code is a little bit
+ posa_card = self.posa_cards.where(requires_purchase: true).where(purchased:false).order(:created_at).first
+ else
+ # the user has at some point b4 indicated interest in a package; so in absence of above indicators, this is what they are buying
+ lesson_package_type = self.desired_package
end
- if lesson_package_type.nil?
- lesson_package_type = LessonPackageType.test_drive_4
- end
-
-
- result = Sale.purchase_test_drive(self, lesson_package_type, booking)
+ result = Sale.purchase_test_drive(self, lesson_package_type, booking, posa_card)
test_drive = result[:sale]
purchase = result[:purchase]
+ if posa_card && !purchase.errors.any?
+ posa_card.has_been_purchased(false)
+ end
if booking && !purchase.errors.any?
# the booking would not have a lesson_package_purchase associated yet, so let's associate it
booking.lesson_sessions.update_all(lesson_package_purchase_id: purchase.id)
@@ -2233,6 +2264,7 @@ module JamRuby
end
+
{lesson: booking, test_drive: test_drive, purchase: purchase, lesson_package_type: lesson_package_type, uncollectables: uncollectables, package: choice}
end
@@ -2256,6 +2288,10 @@ module JamRuby
lesson_purchases.where('lesson_package_type_id in (?)', LessonPackageType.test_drive_package_ids).where('posa_card_id is not null').order('created_at desc').first
end
+ def most_recent_posa_card
+ posa_cards.where('lesson_package_type_id in (?)', LessonPackageType.test_drive_package_ids).order('created_at desc').first
+ end
+
def most_recent_test_drive_purchase
lesson_purchases.where('lesson_package_type_id in (?)', LessonPackageType.test_drive_package_ids).order('created_at desc').first
end
diff --git a/ruby/lib/jam_ruby/resque/scheduled/hourly_job.rb b/ruby/lib/jam_ruby/resque/scheduled/hourly_job.rb
index d9097d3c6..c603a1f51 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/hourly_job.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/hourly_job.rb
@@ -12,6 +12,7 @@ module JamRuby
LessonSession.hourly_check
TeacherPayment.hourly_check
+
@@log.debug("done")
end
end
diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb
index e2ed04d33..15e948589 100644
--- a/ruby/spec/factories.rb
+++ b/ruby/spec/factories.rb
@@ -901,7 +901,24 @@ FactoryGirl.define do
factory :posa_card, class: 'JamRuby::PosaCard' do
sequence(:code) { |n| n.to_s }
- card_type JamRuby::PosaCardType::JAM_TRACKS_5
+ card_type JamRuby::PosaCard::JAM_TRACKS_5
+ credits 5
+ requires_purchase false
+ purchased true
+
+ factory :posa_card_lesson_2 do
+ card_type JamRuby::PosaCard::JAM_CLASS_2
+ credits 2
+ lesson_package_type { JamRuby::LessonPackageType.test_drive_2 }
+ is_lesson true
+ end
+
+ factory :posa_card_lesson_4 do
+ card_type JamRuby::PosaCard::JAM_CLASS_4
+ credits 4
+ lesson_package_type { JamRuby::LessonPackageType.test_drive_4 }
+ is_lesson true
+ end
end
factory :posa_card_type, class: 'JamRuby::PosaCardType' do
diff --git a/ruby/spec/jam_ruby/flows/monthly_recurring_lesson_spec.rb b/ruby/spec/jam_ruby/flows/monthly_recurring_lesson_spec.rb
index f231f9d38..4534f3e2d 100644
--- a/ruby/spec/jam_ruby/flows/monthly_recurring_lesson_spec.rb
+++ b/ruby/spec/jam_ruby/flows/monthly_recurring_lesson_spec.rb
@@ -241,6 +241,47 @@ describe "Monthly Recurring Lesson Flow" do
user.reload
user.remaining_test_drives.should eql 0
UserMailer.deliveries.length.should eql 0 # one for student
+
+ booking.remaining_roll_forward_amount_in_cents.should eql 0
+ lesson_purchase.remaining_roll_forward_amount_in_cents.should be_nil
+ lesson_purchase.expected_session_times.should eql 2
+ lesson_purchase.actual_session_times.should be_nil
+
+ Timecop.travel(Date.new(2016, 4, 1))
+ LessonBooking.adjust_for_missed_sessions
+ # we should find remaining/totals in lesson purcahse as well as booking rolled up
+ booking.reload
+ lesson_purchase.reload
+ lesson_purchase.actual_session_times.should eql 1
+ lesson_purchase.expected_session_times.should eql 2
+ booking.remaining_roll_forward_amount_in_cents.should eql (lesson_purchase.price * 100 * (1.0/2.0)).round
+ lesson_purchase.remaining_roll_forward_amount_in_cents.should eql (lesson_purchase.price * 100 * (1.0/2.0)).round
+ lesson_purchase.total_roll_forward_amount_in_cents.should eql (lesson_purchase.price * 100 * (1.0/2.0)).round
+
+ # but once we bill out, we'll not credit as heavily
+ LessonBooking.bill_monthlies
+ booking.reload
+ lesson_purchase.reload
+ lesson_purchase.actual_session_times.should eql 1
+ lesson_purchase.expected_session_times.should eql 2
+ booking.remaining_roll_forward_amount_in_cents.should eql 0
+ lesson_purchase.remaining_roll_forward_amount_in_cents.should eql 0
+ lesson_purchase.total_roll_forward_amount_in_cents.should eql (lesson_purchase.price * 100 * (1.0/2.0)).round
+
+
+ booking.lesson_package_purchases.count.should eql 2
+ next_purchase = booking.lesson_package_purchases.order(:created_at)[1]
+ next_purchase.remaining_roll_forward_amount_in_cents.should be nil
+ next_purchase.total_roll_forward_amount_in_cents.should be nil
+ next_purchase.expected_session_times.should eql 4
+ next_purchase.actual_session_times.should be_nil
+
+ next_purchase.teacher_distributions.count.should eql 1
+ distribution = next_purchase.teacher_distributions[0]
+ distribution.amount_in_cents.should eql (3000 - 750) # booked price is 30. one lesson is 7.50
+ distribution.teacher_fee_in_cents.should eql ((3000 - 750) * 0.28).round # booked price is 30. one lesson is 7.50. take out .28
+ distribution.reduced_roll_forward_amount_in_cents.should eql 750
+ next_purchase.reduced_roll_forward_amount_in_cents.should eql 750
end
it "works (school on school education)" do
diff --git a/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb b/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
index 10c2e30fa..cfd1dc8d7 100644
--- a/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
+++ b/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
@@ -16,7 +16,7 @@ describe "TestDrive Lesson Flow" do
let(:affiliate_partner) { FactoryGirl.create(:affiliate_partner) }
let(:affiliate_partner2) { FactoryGirl.create(:affiliate_partner, lesson_rate: 0.30) }
let(:school) { FactoryGirl.create(:school) }
- let(:card_lessons) {FactoryGirl.create(:posa_card, card_type: JamRuby::PosaCardType::JAM_CLASS_4)}
+ let(:card_lessons) {FactoryGirl.create(:posa_card_lesson_4)}
let(:retailer) {FactoryGirl.create(:retailer)}
before {
@@ -271,6 +271,7 @@ describe "TestDrive Lesson Flow" do
booking.user.should eql user
booking.sent_notices.should be_true
booking.posa_card.should eql card_lessons
+ booking.posa_card_purchased.should be_true
user.unprocessed_test_drive.should be_nil
teacher_user.has_booked_test_drive_with_student?(user).should be_true
diff --git a/ruby/spec/jam_ruby/models/lesson_package_type_spec.rb b/ruby/spec/jam_ruby/models/lesson_package_type_spec.rb
new file mode 100644
index 000000000..7ed389500
--- /dev/null
+++ b/ruby/spec/jam_ruby/models/lesson_package_type_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe LessonPackageType do
+
+ it "test drive packages" do
+ # 3 builtt-in, 3 amazon
+ LessonPackageType.test_drive_package_ids.count.should eql 6
+ end
+
+ describe "amazon price" do
+ it "4-count" do
+ LessonPackageType.amazon_test_drive_free_4.price.should eql 0
+ end
+
+ it "2-count" do
+ LessonPackageType.amazon_test_drive_free_2.price.should eql 0
+ end
+
+ it "4-count reduced" do
+ LessonPackageType.amazon_test_drive_paid_4.price.should eql 19.99
+ end
+
+ end
+end
diff --git a/ruby/spec/jam_ruby/models/lesson_session_monthly_price_spec.rb b/ruby/spec/jam_ruby/models/lesson_session_monthly_price_spec.rb
index 0f254d5e7..703d6b529 100644
--- a/ruby/spec/jam_ruby/models/lesson_session_monthly_price_spec.rb
+++ b/ruby/spec/jam_ruby/models/lesson_session_monthly_price_spec.rb
@@ -26,7 +26,9 @@ describe LessonSessionMonthlyPrice do
booking.booked_price.should eql 30.00
- booking.booked_price.should eql(LessonSessionMonthlyPrice.price(booking, jan1))
+ price, times = LessonSessionMonthlyPrice.price(booking, jan1)
+ booking.booked_price.should eql(price)
+ times.should eql(5)
end
it "middle of the month" do
@@ -38,7 +40,10 @@ describe LessonSessionMonthlyPrice do
booking.booked_price.should eql 30.00
- ((booking.booked_price * 0.75).round(2)).should eql(LessonSessionMonthlyPrice.price(booking, jan17))
+ price, times = LessonSessionMonthlyPrice.price(booking, jan17)
+ ((booking.booked_price * 0.75).round(2)).should eql(price)
+ times.should eql(3)
+
end
it "end of the month which has a last billable day based on slot" do
@@ -50,7 +55,9 @@ describe LessonSessionMonthlyPrice do
booking.booked_price.should eql 30.00
- ((booking.booked_price * 0.25).round(2)).should eql(LessonSessionMonthlyPrice.price(booking, jan17))
+ price, times = LessonSessionMonthlyPrice.price(booking, jan17)
+ ((booking.booked_price * 0.25).round(2)).should eql(price)
+ times.should eql(1)
end
it "end of the month which is not a last billable days" do
@@ -62,7 +69,9 @@ describe LessonSessionMonthlyPrice do
booking.booked_price.should eql 30.00
- ((booking.booked_price * 0.0).round(2)).should eql(LessonSessionMonthlyPrice.price(booking, feb29))
+ price, times = LessonSessionMonthlyPrice.price(booking, feb29)
+ ((booking.booked_price * 0.0).round(2)).should eql(price)
+ times.should eql(0)
end
end
end
diff --git a/ruby/spec/jam_ruby/models/posa_card_spec.rb b/ruby/spec/jam_ruby/models/posa_card_spec.rb
index cfa886a9c..147101851 100644
--- a/ruby/spec/jam_ruby/models/posa_card_spec.rb
+++ b/ruby/spec/jam_ruby/models/posa_card_spec.rb
@@ -5,7 +5,8 @@ describe PosaCard do
let(:user) {FactoryGirl.create(:user)}
let(:card) {FactoryGirl.create(:posa_card)}
let(:card2) {FactoryGirl.create(:posa_card)}
- let(:card_lessons) {FactoryGirl.create(:posa_card, card_type: JamRuby::PosaCardType::JAM_CLASS_4)}
+ let(:card_lessons) {FactoryGirl.create(:posa_card_lesson_4)}
+ let(:card_lessons_2) {FactoryGirl.create(:posa_card_lesson_2)}
let(:retailer) {FactoryGirl.create(:retailer)}
it "created by factory" do
card.touch
@@ -42,10 +43,12 @@ describe PosaCard do
end
describe "claim" do
- it "succeeds" do
+ it "succeeds simple" do
PosaCard.activate(card, retailer)
card.reload
card.claim(user)
+ user.reload
+ user.jamclass_credits.should be 0
card.errors.any?.should be false
card.claimed_at.should_not be_nil
@@ -56,16 +59,33 @@ describe PosaCard do
PosaCard.activate(card_lessons, retailer)
card_lessons.reload
card_lessons.claim(user)
+ user.jamclass_credits.should be 4
card_lessons.errors.any?.should be false
card_lessons.claimed_at.should_not be_nil
card_lessons.user.should eql user
card_lessons.reload
card_lessons.lesson_package_purchase.should_not be_nil
- card_lessons.lesson_package_purchase.lesson_package_type.should eql LessonPackageType.test_drive_4
+ card_lessons.lesson_package_purchase.lesson_package_type.should eql card_lessons.lesson_package_type
card_lessons.lesson_package_purchase.posa_card.should eql card_lessons
end
+ it "succeeds with jamclass type 2-count" do
+ PosaCard.activate(card_lessons_2, retailer)
+ card_lessons_2.reload
+ card_lessons_2.claim(user)
+ user.reload
+ user.jamclass_credits.should be 2
+
+ card_lessons_2.errors.any?.should be false
+ card_lessons_2.claimed_at.should_not be_nil
+ card_lessons_2.user.should eql user
+ card_lessons_2.reload
+ card_lessons_2.lesson_package_purchase.should_not be_nil
+ card_lessons_2.lesson_package_purchase.lesson_package_type.should eql card_lessons_2.lesson_package_type
+ card_lessons_2.lesson_package_purchase.posa_card.should eql card_lessons_2
+ end
+
it "associates student automatically for GC" do
gc = GuitarCenter.init
gc_owner = gc[:user]
@@ -83,7 +103,7 @@ describe PosaCard do
card_lessons.user.should eql user
card_lessons.reload
card_lessons.lesson_package_purchase.should_not be_nil
- card_lessons.lesson_package_purchase.lesson_package_type.should eql LessonPackageType.test_drive_4
+ card_lessons.lesson_package_purchase.lesson_package_type.should eql card_lessons.lesson_package_type
card_lessons.lesson_package_purchase.posa_card.should eql card_lessons
end
@@ -120,16 +140,16 @@ describe PosaCard do
end
it "can't be within one year" do
- PosaCard.activate(card, retailer)
- card.reload
- card.claim(user)
+ PosaCard.activate(card_lessons, retailer)
+ card_lessons.reload
+ card_lessons.claim(user)
- PosaCard.activate(card2, retailer)
- card2.reload
- card2.claim(user)
+ PosaCard.activate(card_lessons_2, retailer)
+ card_lessons_2.reload
+ card_lessons_2.claim(user)
- card2.errors.any?.should be true
- card2.errors[:user].should eql ['was within 1 year']
+ card_lessons_2.errors.any?.should be true
+ card_lessons_2.errors[:claimed_at].should eql ['was within 1 year']
end
end
end
\ No newline at end of file
diff --git a/ruby/spec/jam_ruby/models/sale_spec.rb b/ruby/spec/jam_ruby/models/sale_spec.rb
index adcd5a147..b01b773b9 100644
--- a/ruby/spec/jam_ruby/models/sale_spec.rb
+++ b/ruby/spec/jam_ruby/models/sale_spec.rb
@@ -889,6 +889,7 @@ describe Sale do
end
it "can succeed with no booking; just intent" do
+ user.desired_package = LessonPackageType.test_drive_4
intent = TeacherIntent.create(user, teacher, 'book-test-drive')
token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', test_drive: true})
@@ -912,6 +913,7 @@ describe Sale do
end
it "will reject second test drive purchase" do
+ user.desired_package = LessonPackageType.test_drive_4
intent = TeacherIntent.create(user, teacher, 'book-test-drive')
token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', test_drive: true})
diff --git a/ruby/spec/jam_ruby/models/user_spec.rb b/ruby/spec/jam_ruby/models/user_spec.rb
index c74e78f49..3d3bfe62f 100644
--- a/ruby/spec/jam_ruby/models/user_spec.rb
+++ b/ruby/spec/jam_ruby/models/user_spec.rb
@@ -710,7 +710,7 @@ describe User do
user.age.should == 9
user.birth_date = nil
- user.age.should == ""
+ user.age.should == nil
end
end
@@ -730,7 +730,7 @@ describe User do
end
- it "allow no_show aggregation" do
+ it "allow no_show aggr egation" do
user.mod_merge({"no_show" => {"some_screen1" => true}})
user.save!
user.reload
diff --git a/ruby/spec/support/utilities.rb b/ruby/spec/support/utilities.rb
index 01ce103cb..1b195b0f1 100644
--- a/ruby/spec/support/utilities.rb
+++ b/ruby/spec/support/utilities.rb
@@ -19,6 +19,10 @@ def app_config
'alerts@jamkazam.com'
end
+ def email_jamclass_alerts_alias
+ 'jamclass-alerts@jamkazam.com'
+ end
+
def email_crashes_alias
'clientcrash@jamkazam.com'
end
diff --git a/web/app/assets/javascripts/dialog/shutdownDialog.js b/web/app/assets/javascripts/dialog/shutdownDialog.js
index 061dd8017..5390f224d 100644
--- a/web/app/assets/javascripts/dialog/shutdownDialog.js
+++ b/web/app/assets/javascripts/dialog/shutdownDialog.js
@@ -11,6 +11,10 @@
context.JK.Banner.showAlert(
{ title: "Close JamKazam Application",
buttons: [
+ {name: 'CANCEL SHUTDOWN', click: function() {
+ logger.debug("'CANCEL SHUTDOWN' selected")
+ context.JK.Banner.hide();
+ }},
{name: 'COMPLETELY SHUT DOWN THE APP', click: function() {
logger.debug("'COMPLETELY SHUT DOWN THE APP' selected")
context.jamClient.ShutdownApplication()
diff --git a/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee b/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee
index 37f903af4..afc89d3a1 100644
--- a/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee
@@ -100,7 +100,7 @@ UserStore = context.UserStore
userDetailDone: (response) ->
if response.id == @state.teacherId
#school_on_school = response.teacher.school_id? && @state.user?.school_id? && response.teacher.school_id == @state.user.school_id && !response.teacher.school.education
- @setState({teacher: response, isSelf: response.id == context.JK.currentUserId, school_on_school: school_on_school})
+ @setState({teacher: response, isSelf: response.id == context.JK.currentUserId})
else
logger.debug("BookLesson: ignoring teacher details", response.id, @state.teacherId)
@@ -235,7 +235,7 @@ UserStore = context.UserStore
@setState({updating: false})
UserActions.refresh()
#if response.user['has_stored_credit_card?'] || @state.school_on_school || response.posa_card_id?
- if response.user['has_stored_credit_card?'] || response.posa_card_id?
+ if response.user['has_stored_credit_card?'] || (response.posa_card_id? && response.posa_card_purchased)
context.JK.Banner.showNotice("Lesson Requested","The teacher has been notified of your lesson request, and should respond soon.
We've taken you back to the JamClass home page, where you can check the status of this lesson, as well as any other past and future lessons.")
url = "/client#/jamclass/lesson-booking/#{response.id}"
url = "/client#/jamclass"
@@ -302,16 +302,17 @@ UserStore = context.UserStore
else
for minutes in enabledMinutes
lesson_price = teacher["price_per_lesson_#{minutes}_cents"]
- value = "single|#{minutes}"
- display = "#{minutes} Minute Lesson Each Week - $#{(lesson_price / 100).toFixed(2)} Per Week"
- results.push(``)
-
+ if lesson_price
+ value = "single|#{minutes}"
+ display = "#{minutes} Minute Lesson Each Week - $#{(lesson_price / 100).toFixed(2)} Per Week"
+ results.push(``)
for minutes in enabledMinutes
monthly_price = teacher["price_per_month_#{minutes}_cents"]
- value = "monthly|#{minutes}"
- display = "#{minutes} Minute Lesson Each Week - $#{(monthly_price / 100).toFixed(2)} Per Month"
- results.push(``)
+ if monthly_price
+ value = "monthly|#{minutes}"
+ display = "#{minutes} Minute Lesson Each Week - $#{(monthly_price / 100).toFixed(2)} Per Month"
+ results.push(``)
if results.length == 0
results.push(``)
@@ -348,8 +349,8 @@ UserStore = context.UserStore
am_pm = [``, ``]
- bookLessonClasses = classNames({"button-orange": true, 'book-lesson-btn': true, disabled: !this.state.teacher? && !@state.updating})
- cancelClasses = classNames({"button-grey": true, disabled: !this.state.teacher? && !@state.updating})
+ bookLessonClasses = classNames({"button-orange": true, 'book-lesson-btn': true, disabled: !this.state.teacher? || @state.updating})
+ cancelClasses = classNames({"button-grey": true, disabled: !this.state.teacher? || @state.updating})
descriptionErrors = context.JK.reactSingleFieldErrors('description', @state.descriptionErrors)
bookedPriceErrors = context.JK.reactSingleFieldErrors('booked_price', @state.bookedPriceErrors)
diff --git a/web/app/assets/javascripts/react-components/JamClassScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/JamClassScreen.js.jsx.coffee
index a3062a088..08f901183 100644
--- a/web/app/assets/javascripts/react-components/JamClassScreen.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/JamClassScreen.js.jsx.coffee
@@ -233,6 +233,11 @@ LessonTimerActions = context.LessonTimerActions
rescheduleLesson: (lesson) ->
if lesson.recurring
buttons = []
+ buttons.push({
+ name: 'CANCEL',
+ buttonStyle: 'button-grey',
+ click: (() => (logger.debug("cancelling out of reschedule dialog")))
+ })
buttons.push({
name: 'THIS LESSON',
buttonStyle: 'button-orange',
diff --git a/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee b/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee
index 2f70c9221..e58ec7a77 100644
--- a/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee
@@ -39,7 +39,7 @@ UserStore = context.UserStore
return
slot.slotTime = @slotTime(slot, booking)
- slot.is_recurring = @isRecurring()
+ slot.is_recurring = slot.slot_type == 'recurring'
if slot['is_teacher_created?']
slot.creatorRole = 'teacher'
slot.creatorRoleRelative = 'teacher'
@@ -169,6 +169,7 @@ UserStore = context.UserStore
@focusedLesson()?
focusedLesson: () ->
+ console.log("focusedlesos", this.state?.booking?.focused_lesson)
this.state?.booking?.focused_lesson
updateBookingState: (booking) ->
@@ -425,10 +426,22 @@ UserStore = context.UserStore
false
isApproved: () ->
- @state.booking?.status == 'approved'
+ if @hasFocusedLesson()
+ @focusedLesson().status == 'approved'
+ else
+ @state.booking?.status == 'approved'
isCanceled: () ->
- @state.booking?.status == 'canceled'
+
+ cancelled = @state.booking?.status == 'canceled'
+ if cancelled
+ # if the booking is canceelled, lessons are done too. No need to check the focused lesson
+ return true
+ else
+ if @hasFocusedLesson()
+ return @focusedLesson().status == 'canceled'
+ else
+ cancelled
isSuspended: () ->
@state.booking?.status == 'suspended'
@@ -498,13 +511,15 @@ UserStore = context.UserStore
text = "Preferred day/time for lesson is #{this.slotTime(defaultSlot)}. Secondary option is #{this.slotTime(altSlot)}."
slotTime: (slot, booking = this.state.booking) ->
- if @hasFocusedLesson() || !@isRecurring(booking)
+ if @hasFocusedLesson() || slot.slot_type == 'single' #!@isRecurring(booking)
slot.pretty_start_time
else
"#{this.dayOfWeek(slot)} at #{this.dayTime(slot)}"
slotTimePhrase: (slot) ->
- if @isRecurring()
+ console.log("SLOT", slot)
+ if slot.slot_type == 'recurring'
+ # if @isRecurring()
"each " + @slotTime(slot)
else
@slotTime(slot)
@@ -709,6 +724,10 @@ UserStore = context.UserStore
header = 'respond to lesson request'
content = @renderTeacherRequested()
+ else if @isApproved()
+ header = @approvedHeader()
+ content = @renderTeacherApproved()
+
else if @isCounter()
if @isTeacherCountered()
header = 'your proposed alternate day/time is still pending'
@@ -716,10 +735,6 @@ UserStore = context.UserStore
header = 'student has proposed an alternate day/time'
content = @renderTeacherCountered()
- else if @isApproved()
- header = @approvedHeader()
- content = @renderTeacherApproved()
-
else if @isCompleted()
header = @completedHeader()
content = @renderTeacherComplete()
@@ -750,6 +765,11 @@ UserStore = context.UserStore
header = 'your lesson has been requested'
content = @renderStudentRequested()
+ else if @isApproved()
+ header = @approvedHeader()
+ content = @renderStudentApproved()
+
+
else if @isCounter()
if @isTeacherCountered()
header = 'teacher has proposed an alternate day/time'
@@ -757,10 +777,6 @@ UserStore = context.UserStore
header = 'your proposed alternate day/time is still pending'
content = @renderTeacherCountered()
- else if @isApproved()
- header = @approvedHeader()
- content = @renderStudentApproved()
-
else if @isCompleted()
header = @completedHeader()
content = @renderStudentComplete()
diff --git a/web/app/assets/javascripts/react-components/LessonBookingDecision.js.jsx.coffee b/web/app/assets/javascripts/react-components/LessonBookingDecision.js.jsx.coffee
index b63955402..697ff2d77 100644
--- a/web/app/assets/javascripts/react-components/LessonBookingDecision.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/LessonBookingDecision.js.jsx.coffee
@@ -251,7 +251,7 @@
proposeAltLabelText = "Propose alternate day/time"
for slot, i in @props.slots
- if this.props.is_recurring
+ if slot.is_recurring
slotDetail = `
Each {slot.slotTime}
`
else
slotDetail = `
{slot.slotTime}
`
diff --git a/web/app/assets/javascripts/react-components/LessonPayment.js.jsx.coffee b/web/app/assets/javascripts/react-components/LessonPayment.js.jsx.coffee
index f8ba02b5d..326660e43 100644
--- a/web/app/assets/javascripts/react-components/LessonPayment.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/LessonPayment.js.jsx.coffee
@@ -307,47 +307,46 @@ UserStore = context.UserStore
if response.test_drive?
# ok, they bought a package
if response.lesson_package_type?
- # always of form test-drive-#
- prefixLength = "test-drive-".length
- packageLength = response.lesson_package_type.package_type.length
- testDriveCount = response.lesson_package_type.package_type.substring(prefixLength, packageLength)
+ testDriveCount = response.lesson_package_type.credits
logger.debug("testDriveCount: " + testDriveCount)
- testDriveCountInt = parseInt(testDriveCount);
- if context._.isNaN(testDriveCountInt)
- testDriveCountInt = 3
+ context.JK.GA.trackTestDrivePurchase(testDriveCount);
- context.JK.GA.trackTestDrivePurchase(testDriveCountInt);
-
- if response.test_drive?.teacher_id
- teacher_id = response.test_drive.teacher_id
- if testDriveCount == '1'
+ if response.package?
+ logger.debug("packaged TD purchase...")
+ # the user bought a package that already includes the 1, 2, or 4 teachers as part of it
+ if testDriveCount == 1
text = "You have purchased 1 TestDrive credit and have used it to request a JamClass with #{@state.package.teachers[0].user.name}. The teacher has received your request and should respond shortly."
- else if response.package?
+ location = "/client#/jamclass"
+ else
text = "Each teacher has received your request and should respond shortly."
- else
- text = "You have purchased #{testDriveCount} TestDrive credits and have used 1 credit to request a JamClass with #{@state.teacher.name}. The teacher has received your request and should respond shortly."
+ location = "/client#/jamclass"
+ else if response.lesson?
+ logger.debug("TD with a lesson booking...")
+ # there is a lesson booking in context, so the user has gotten a credit + gotten a lesson request going forward
+ text = "You have purchased #{testDriveCount} TestDrive credits and have used 1 credit to request a JamClass with #{@state.teacher?.name}. The teacher has received your request and should respond shortly."
location = "/client#/jamclass"
- else
- if @state.teacher?.id
-
- # the user bought the testdrive, and there is a teacher of interest in context (but no booking)
- if testDriveCount == '1'
- text = "You now have 1 TestDrive credit.
We've taken you to the lesson booking screen for the teacher you initially showed interest in."
- location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
- else
- text = "You now have #{testDriveCount} TestDrive credits that you can take with #{testDriveCount} different teachers.
We've taken you to the lesson booking screen for the teacher you initially showed interest in."
- location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
+ else if response.teacher?.id
+ logger.debug("straight TD package + teacher interest")
+ # the user bought a test drive straight up with no lesson-booking, but previously showed interest in a teacher
+ teacher_id = response.teacher.id
+ if testDriveCount == '1'
+ text = "You now have 1 TestDrive credit.
We've taken you to the lesson booking screen for the teacher you initially showed interest in."
+ location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
else
- # the user bought test drive, but 'cold' , i.e., no teacher in context
- if testDriveCount == '1'
- text = "You now have 1 TestDrive credit.
We've taken you to the Teacher Search screen, so you can search for teachers right for you."
- location = "/client#/teachers/search"
- else
- text = "You now have #{testDriveCount} TestDrive credits that you can take with #{testDriveCount} different teachers.
We've taken you to the Teacher Search screen, so you can search for teachers right for you."
- location = "/client#/teachers/search"
+ text = "You now have #{testDriveCount} TestDrive credits that you can take with #{testDriveCount} different teachers.
We've taken you to the lesson booking screen for the teacher you initially showed interest in."
+ location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
+ else
+ logger.debug("straight TD package")
+ # the user bought test drive, but 'cold' , and, no teacher in context
+ if testDriveCount == 1
+ text = "You now have 1 TestDrive credit.
We've taken you to the Teacher Search screen, so you can search for teachers right for you."
+ location = "/client#/teachers/search"
+ else
+ text = "You now have #{testDriveCount} TestDrive credits that you can take with #{testDriveCount} different teachers.
We've taken you to the Teacher Search screen, so you can search for teachers right for you."
+ location = "/client#/teachers/search"
context.JK.Banner.showNotice("TestDrive Purchased",text)
window.location = location
@@ -495,14 +494,24 @@ UserStore = context.UserStore
else
alert("unknown package type")
else
- if this.state.user.lesson_package_type_id == 'test-drive'
+ # the UI is currenty the same whether the user is browsing around indicating what type of TD they want, or
+ # if they bought a POSA card required puchase, and have the this.state.user.lesson_package_needs_purchase_id
+ # we just need to figure out which is set, giving preference to the POSA requiring purchase
+ lesson_package_type_id = this.state.user.lesson_package_needs_purchase_id
+ if !lesson_package_type_id?
+ lesson_package_type_id = this.state.user.lesson_package_type_id
+
+ if lesson_package_type_id == 'test-drive'
explanation = `You are purchasing the TestDrive package of JamClass by JamKazam. This purchase entitles you to take 4 private online music lessons - 1 each from 4 different instructors in the JamClass instructor community. The price of this TestDrive package is $49.99.`
- else if this.state.user.lesson_package_type_id == 'test-drive-1'
+ else if lesson_package_type_id == 'test-drive-1'
explanation =`You are purchasing the TestDrive package of JamClass by JamKazam. This purchase entitles you to take 1 private online music lesson from an instructor in the JamClass instructor community. The price of this TestDrive package is $14.99.`
- else if this.state.user.lesson_package_type_id == 'test-drive-2'
+ else if lesson_package_type_id == 'test-drive-2'
explanation =`You are purchasing the TestDrive package of JamClass by JamKazam. This purchase entitles you to take 2 private online music lessons - 1 each from 2 different instructors in the JamClass instructor community. The price of this TestDrive package is $29.99.`
+ else if lesson_package_type_id == 'amazon-test-drive-paid-4'
+ explanation = `You are purchasing the TestDrive package of JamClass by JamKazam. This purchase entitles you to take 4 private online music lessons - 1 each from 4 different instructors in the JamClass instructor community. The price of this TestDrive package is $19.99.`
else
- alert("You do not have a test drive package selected: " + this.state.user.lesson_package_type_id )
+ explanation = `Loading your TestDrive packaging info...`
+ #alert("You do not have a test drive package selected: " + this.state.user.lesson_package_type_id )
bookingDetail = `
`
else if this.state.lesson.payment_style == 'weekly'
bookingInfo = `
You are booking a weekly recurring series of {lesson_length}-minute
- lessons, to be paid individually as each lesson is taken, until cancelled.
`
+ lessons for ${this.bookedPrice()}, to be paid individually as each lesson is taken, until cancelled.
`
bookingDetail = `
Your card will be charged on the day of each lesson. If you need to cancel a lesson, you must do so at
least 24 hours before the lesson is scheduled, or you will be charged for the lesson in full.
@@ -542,7 +551,7 @@ UserStore = context.UserStore
`
else if this.state.lesson.payment_style == 'monthly'
bookingInfo = `
You are booking a weekly recurring series of {lesson_length}-minute
- lessons, to be paid for monthly until cancelled.
`
+ lessons for ${this.bookedPrice()}, to be paid for monthly until cancelled.`
bookingDetail = `
Your card will be charged on the first day of each month. Canceling individual lessons does not earn a
refund when buying monthly. To cancel, you must cancel at least 24 hours before the beginning of the
diff --git a/web/app/assets/javascripts/react-components/TeacherProfile.js.jsx.coffee b/web/app/assets/javascripts/react-components/TeacherProfile.js.jsx.coffee
index 43d0a0902..900c737fd 100644
--- a/web/app/assets/javascripts/react-components/TeacherProfile.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/TeacherProfile.js.jsx.coffee
@@ -81,7 +81,7 @@ proficiencyDescriptionMap = {
# mount it
@profileClipboard = new Clipboard($profileLink.get(0), {
text: =>
- return context.JK.makeAbsolute('/client#/teacher/profile/' + @state.user.teacher?.id)
+ return context.JK.makeAbsolute('/client#/teacher/profile/' + @state.user.id)
})
else if $profileLink.length == 0 && @profileClipboard?
@profileClipboard.destroy()
@@ -245,10 +245,15 @@ proficiencyDescriptionMap = {
biography = biography.replace(/\n/g, " ")
- `
`
diff --git a/web/app/assets/javascripts/react-components/TeacherSearchScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/TeacherSearchScreen.js.jsx.coffee
index c15e12db1..2a2e2fea4 100644
--- a/web/app/assets/javascripts/react-components/TeacherSearchScreen.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/TeacherSearchScreen.js.jsx.coffee
@@ -137,10 +137,10 @@ ProfileActions = @ProfileActions
if @state.next == null
@contentBodyScroller.off('scroll')
- if @state.currentPage == 1 and @state.results.length == 0
+ if @state.currentPage == 1 and @state.results.length == 0 && !@state.searching
@endOfList.text('No Teachers found matching your search').show()
logger.debug("TeacherSearch: empty search")
- else if @state.currentPage > 0
+ else if @state.currentPage > 0 && !@state.searching
logger.debug("end of search")
@endOfList.text('No more Teachers').show()
else
@@ -238,13 +238,16 @@ ProfileActions = @ProfileActions
bookSingleBtn = null
bookTestDriveBtn = null
-
+ backgroundCheck = null
+ if user.teacher.background_check_at
+ backgroundCheck = ``
if !school_on_school && (!@state.user? || @state.user.jamclass_credits > 0 || @state.user.remaining_test_drives > 0 || @state.user['can_buy_test_drive?'])
bookTestDriveBtn = `BOOK TESTDRIVE LESSON`
else
bookSingleBtn = `BOOK LESSON`
resultsJsx.push(`
+ {backgroundCheck}
diff --git a/web/app/assets/javascripts/react-components/landing/AccountActivatePage.js.jsx.coffee b/web/app/assets/javascripts/react-components/landing/AccountActivatePage.js.jsx.coffee
new file mode 100644
index 000000000..04f3456d4
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/landing/AccountActivatePage.js.jsx.coffee
@@ -0,0 +1,200 @@
+context = window
+rest = context.JK.Rest()
+ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
+
+badCode = 'This is not a valid code. Please carefully re-enter the code and try again. If it still does not work, please email us at support@jamkazam.com to report this problem.'
+@AccountActivatePage = React.createClass({
+
+ render: () ->
+
+
+ if this.state.formErrors?
+ for key, value of this.state.formErrors
+ break
+
+ errorText = context.JK.getFullFirstError(key, @state.formErrors, {email: 'Email', password: 'Password', gift_card: 'Coupon Code', posa_cards: 'Coupon Code', 'terms_of_service' : 'The terms of service', claimed_at: "Last coupon code used"})
+
+ if errorText? && errorText.indexOf('does not exist') > -1
+ errorText = 'This is not a valid code. Please carefully re-enter the code and try again. If it still does not work, please email us at support@jamkazam.com to report this problem.'
+
+ if errorText? && errorText.indexOf('must already be set') > -1
+ errorText = 'This card has not been activated by a retailer and cannot currently be used. If you purchased this card from a store, please return to the store and have the store activate the card. Only the store where you purchased this card can activate it.'
+
+ if errorText? && errorText.indexOf('already claimed') > -1
+ errorText = 'This card has already been claimed. If you believe this is in error, please email us at support@jamkazam.com to report this problem.'
+
+ buttonClassnames = classNames({'redeem-giftcard': true, 'button-orange': true, disabled: @state.processing || @state.done })
+
+
+ if @state.done
+ if this.state.gifted_jamtracks
+
+ button =
+ `
+
You now have {this.state.gifted_jamtracks} JamTracks credits on your account!