lots of progress on amazon-readiness

This commit is contained in:
Seth Call 2017-07-09 21:21:29 -05:00
parent cd9525ac2b
commit 7ff7327ef8
79 changed files with 1586 additions and 353 deletions

View File

@ -59,7 +59,7 @@ gem 'resque'
gem 'resque-retry' gem 'resque-retry'
gem 'resque-failed-job-mailer' gem 'resque-failed-job-mailer'
gem 'resque-lonely_job', '~> 1.0.0' gem 'resque-lonely_job', '~> 1.0.0'
gem 'eventmachine', '1.0.4' gem 'eventmachine', '1.2.3'
gem 'amqp', '0.9.8' gem 'amqp', '0.9.8'
#gem 'logging-rails', :require => 'logging/rails' #gem 'logging-rails', :require => 'logging/rails'
gem 'pg_migrate' gem 'pg_migrate'
@ -82,9 +82,9 @@ gem 'stripe'
gem 'zip-codes' gem 'zip-codes'
gem 'email_validator' gem 'email_validator'
group :libv8 do #group :libv8 do
gem 'libv8', "~> 4.5.95" # gem 'libv8', "~> 4.5.95"
end #end
# To use Jbuilder templates for JSON # To use Jbuilder templates for JSON

View File

@ -0,0 +1,40 @@
ActiveAdmin.register JamRuby::User, :as => 'InactiveJamClassUsers' do
menu :label => 'Inactive JamClass Users w/o Credits', :parent => 'JamClass'
config.sort_order = 'created_at'
config.batch_actions = false
config.per_page = 100
config.paginate = true
config.filters = false
scope("All", default: true) { |scope| scope.includes(:taken_lessons => :music_session).select("distinct(users.id), users.email, users.first_name, users.last_name").joins("inner join posa_cards on posa_cards.user_id = users.id inner join lesson_sessions on lesson_sessions.user_id = users.id left outer join music_sessions on music_sessions.lesson_session_id = music_sessions.id ").where("jamclass_credits = 0") }
index do
column "Name" do |user|
span do
link_to "#{user.name} (#{user.email})", "#{Rails.application.config.external_root_url}/client#/profile/#{user.id}"
end
end
column "POSA" do |user|
span do
posa = user.posa_cards[0]
if posa.lesson_package_type
posa.lesson_package_type.id
else
posa.card_type
end
end
end
column "Last Session" do |user|
span do
if user.taken_lessons.length == 0
"none yet"
else
most_recent_lesson = user.taken_lessons.order('created_at desc')[0]
link_to most_recent_lesson.scheduled_start, admin_lesson_session_path(most_recent_lesson)
end
end
end
end
end

View File

@ -0,0 +1,45 @@
ActiveAdmin.register JamRuby::User, :as => 'InactiveJamClassPOSAUsers' do
menu :label => 'Inactive JamClass Users w/ Credits', :parent => 'JamClass'
config.sort_order = 'created_at'
config.batch_actions = false
config.per_page = 100
config.paginate = true
config.filters = false
scope("All", default: true) { |scope| scope.includes(:taken_lessons => :music_session).select("distinct(users.id), users.email, users.first_name, users.last_name, users.jamclass_credits").joins("inner join posa_cards on posa_cards.user_id = users.id left outer join lesson_sessions on lesson_sessions.user_id = users.id left outer join music_sessions on music_sessions.lesson_session_id = music_sessions.id ").where("jamclass_credits > 0 AND music_sessions.id IS NULL OR music_sessions.scheduled_start < ?", Time.now - 7.days) }
index do
column "Name" do |user|
span do
link_to "#{user.name} (#{user.email})", "#{Rails.application.config.external_root_url}/client#/profile/#{user.id}"
end
end
column "POSA" do |user|
span do
posa = user.posa_cards[0]
if posa.lesson_package_type
posa.lesson_package_type.id
else
posa.card_type
end
end
end
column "Credits" do |user|
span do
user.jamclass_credits
end
end
column "Last Session" do |user|
span do
if user.taken_lessons.length == 0
"none yet"
else
most_recent_lesson = user.taken_lessons.order('created_at desc')[0]
link_to most_recent_lesson.scheduled_start, admin_lesson_session_path(most_recent_lesson)
end
end
end
end
end

View File

@ -17,10 +17,16 @@ ActiveAdmin.register JamRuby::LessonSession, :as => 'LessonSessions' do
scope("Completed") { |scope| scope.unscoped.completed.order('created_at desc') } scope("Completed") { |scope| scope.unscoped.completed.order('created_at desc') }
index do index do
column "User Link" do |lesson_session| column "Actions" do |teacher|
links = ''.html_safe
links << link_to("View", resource_path(teacher), :class => "member_link view_link")
links << link_to("Edit", edit_resource_path(teacher), :class => "member_link edit_link")
links
end
column "App Link" do |lesson_session|
lesson_booking = lesson_session.lesson_booking lesson_booking = lesson_session.lesson_booking
span do span do
link_to "Web URL", "#{Rails.application.config.external_root_url}/client#/jamclass/lesson-booking/#{lesson_booking.id}" link_to "link", "#{Rails.application.config.external_root_url}/client#/jamclass/lesson-booking/#{lesson_booking.id}"
end end
end end
column "Status" do |lesson_session| column "Status" do |lesson_session|
@ -58,10 +64,10 @@ ActiveAdmin.register JamRuby::LessonSession, :as => 'LessonSessions' do
show do show do
attributes_table do attributes_table do
row "User Link" do |lesson_session| row "App Link" do |lesson_session|
lesson_booking = lesson_session.lesson_booking lesson_booking = lesson_session.lesson_booking
span do span do
link_to "Web URL", "#{Rails.application.config.external_root_url}/client#/jamclass/lesson-booking/#{lesson_booking.id}" link_to "link", "#{Rails.application.config.external_root_url}/client#/jamclass/lesson-booking/#{lesson_booking.id}"
end end
end end
row "Status" do |lesson_session| row "Status" do |lesson_session|
@ -82,13 +88,13 @@ ActiveAdmin.register JamRuby::LessonSession, :as => 'LessonSessions' do
row "Teacher" do |lesson_session| row "Teacher" do |lesson_session|
teacher = lesson_session.teacher teacher = lesson_session.teacher
span do span do
link_to "#{teacher.name} (#{teacher.email})", "#{Rails.application.config.external_root_url}/client#/profile/teacher/#{teacher.id}" link_to teacher.admin_name, "#{Rails.application.config.external_root_url}/client#/profile/teacher/#{teacher.id}"
end end
end end
row "Student" do |lesson_session| row "Student" do |lesson_session|
student = lesson_session.student student = lesson_session.student
span do span do
link_to "#{student.name} (#{student.email})", "#{Rails.application.config.external_root_url}/client#/profile/#{student.id}" link_to student.admin_name, "#{Rails.application.config.external_root_url}/client#/profile/#{student.id}"
end end
end end
row "Followup Emails Sent" do |lesson_session| row "Followup Emails Sent" do |lesson_session|
@ -111,6 +117,11 @@ ActiveAdmin.register JamRuby::LessonSession, :as => 'LessonSessions' do
lesson_session.timed_description lesson_session.timed_description
end end
end end
row "Session" do |lesson_session|
span do
link_to "Session", lesson_session.music_session.admin_url
end
end
row "Analysis" do |lesson_session| row "Analysis" do |lesson_session|
if lesson_session.analysed if lesson_session.analysed
span style: "white-space: pre;" do span style: "white-space: pre;" do

View File

@ -60,4 +60,51 @@ ActiveAdmin.register JamRuby::MusicSession, :as => 'Music Session' do
end end
end end
show do
attributes_table do
row :id
row :name
row :description
row :creator do |session|
link_to(session.creator.admin_name, session.creator.admin_url)
end
row :created_at
row :started_at
row :session_ended_at do |session| session.session_removed_at end
row :genre
row :recurring_mode
row :timezone
row :fan_access
row :music_access
row :approval_required
row :open_rsvps
row :is_unstructured_rsv
row :canceled
row :lesson_session do |session|
lesson_session = session.lesson_session
if lesson_session
link_to("Lesson", lesson_session.admin_url)
else
''
end
end
row 'Session Attendances' do |session|
table_for(msuh = session.music_session_user_histories) do
column :user do |msuh| msuh.user.admin_name end
column :joined do |msuh| msuh.created_at.strftime('%b %d %Y, %H:%M') end
column :duration do |msuh| "#{msuh.duration_minutes.round} minutes" end
column :perf_data do |msuh|
unless (uu = msuh.perf_uri).blank?
link_to('Per Data Link', uu)
else
'No Perf Data'
end
end
end
end
end
end
end end

View File

@ -10,15 +10,31 @@ ActiveAdmin.register_page "POSA Card Uploads" do
file = params[:jam_ruby_posa_card][:csv] file = params[:jam_ruby_posa_card][:csv]
array_of_arrays = CSV.read(file.tempfile.path) array_of_arrays = CSV.read(file.tempfile.path)
array_of_arrays.each do |row| array_of_arrays.each do |row|
if row.length != 1 if row.length != 4
raise "UKNONWN CSV FORMAT! Must be 1 column" raise "UKNONWN CSV FORMAT! Must be 4 columns"
end end
code = row[0] code = row[0]
lesson_package_type = row[1]
preactivate = row[2].strip == "true"
requires_purchase = row[3].strip == "true"
posa_card = PosaCard.new posa_card = PosaCard.new
posa_card.code = code posa_card.code = code
posa_card.lesson_package_type = LessonPackageType.find(lesson_package_type)
posa_card.preactivate = preactivate
posa_card.requires_purchase = requires_purchase
posa_card.purchased = !requires_purchase
posa_card.card_type = params[:jam_ruby_posa_card][:card_type] posa_card.card_type = params[:jam_ruby_posa_card][:card_type]
if posa_card.card_type == PosaCard::JAM_CLASS_4
posa_card.is_lesson = true
posa_card.credits = 4
elsif posa_card.card_type == PosaCard::JAM_CLASS_2
posa_card.is_lesson = true
posa_card.credits = 2
end
posa_card.origin = file .original_filename posa_card.origin = file .original_filename
posa_card.save! posa_card.save!
end end

View File

@ -45,7 +45,7 @@ ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do
end end
end end
=begin
column "Background Check" do |teacher| column "Background Check" do |teacher|
div do div do
if teacher.background_check_at if teacher.background_check_at
@ -56,25 +56,25 @@ ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do
br br
end end
span do span do
link_to(mark_background_check_admin_teacher_path(teacher.id), {confirm: "Mark as background checked?"}) do link_to(edit_admin_teacher_background_check_path(teacher.id)) do
"mark as checked" "update background check"
end end
end end
else else
span do span do
'' 'NO'
end end
span do span do
br br
end end
span do span do
link_to("mark as checked", mark_background_check_admin_teacher_path(teacher.id), {confirm: "Mark as background checked?"}) link_to("update background check", edit_admin_teacher_background_check_path(teacher.id))
end end
end end
end end
end end
=end
column "Session Ready" do |teacher| column "Session Ready" do |teacher|
div do div do
if teacher.ready_for_session_at if teacher.ready_for_session_at

View File

@ -0,0 +1,31 @@
ActiveAdmin.register JamRuby::Teacher, :as => 'TeacherBackgroundCheck' do
config.filters = false
menu :label => 'Teacher Background Check', :parent => 'JamClass'
form do |f|
f.inputs 'Set Background Check' do
f.input :background_check_at, as: :date_select
end
f.actions
end
index do
column "Actions" do |teacher|
links = ''.html_safe
links << link_to("View", resource_path(teacher), :class => "member_link view_link")
links << link_to("Edit", edit_resource_path(teacher), :class => "member_link edit_link")
links
end
column 'User' do |oo|
oo.user.email
end
end
end

View File

@ -87,7 +87,7 @@ module JamAdmin
config.recurly_root_url = 'https://jamkazam-development.recurly.com' config.recurly_root_url = 'https://jamkazam-development.recurly.com'
# where is rabbitmq? # where is rabbitmq?
config.rabbitmq_host = "localhost" config.rabbitmq_host = "127.0.0.1"
config.rabbitmq_port = 5672 config.rabbitmq_port = 5672
# set to false to instead use amazon. You will also need to supply amazon secrets # set to false to instead use amazon. You will also need to supply amazon secrets

View File

@ -1,5 +1,5 @@
class JamRuby::Teacher class JamRuby::Teacher
attr_accessible :short_bio, as: :admin attr_accessible :short_bio, :background_check_at, as: :admin
end end

View File

@ -1,26 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>The page you were looking for doesn't exist (404)</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
<body>
<!-- This file lives in public/404.html -->
<div class="dialog">
<h1>The page you were looking for doesn't exist.</h1>
<p>You may have mistyped the address or the page may have moved.</p>
</div>
</body>
</html>

View File

@ -1,26 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>The change you wanted was rejected (422)</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
<body>
<!-- This file lives in public/422.html -->
<div class="dialog">
<h1>The change you wanted was rejected.</h1>
<p>Maybe you tried to change something you didn't have access to.</p>
</div>
</body>
</html>

View File

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>We're sorry, but something went wrong (500)</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
<body>
<!-- This file lives in public/500.html -->
<div class="dialog">
<h1>We're sorry, but something went wrong.</h1>
</div>
</body>
</html>

View File

@ -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: /

View File

@ -376,3 +376,4 @@ jam_track_download_rights.sql
guitar_center_integration_v1.sql guitar_center_integration_v1.sql
mobile_recording_support.sql mobile_recording_support.sql
youtube_broadcast.sql youtube_broadcast.sql
amazon_v1.sql

38
db/up/amazon_v1.sql Normal file
View File

@ -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;

View File

@ -20,6 +20,14 @@ module JamRuby
subject: options[:subject]) subject: options[:subject])
end 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) def crash_alert(options)
mail(to: APP_CONFIG.email_crashes_alias, mail(to: APP_CONFIG.email_crashes_alias,
from: APP_CONFIG.email_generic_from, from: APP_CONFIG.email_generic_from,

View File

@ -355,7 +355,7 @@ SQL
if active_music_session if active_music_session
music_session = active_music_session.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 # find next in line, because the current 'session controller' is not part of the session
next_in_line(music_session, active_music_session) next_in_line(music_session, active_music_session)
end end

View File

@ -88,13 +88,12 @@ module JamRuby
# If this is a lesson posa card, then put that user into the guitar center school # If this is a lesson posa card, then put that user into the guitar center school
def self.post_posa_claim(posa) def self.post_posa_claim(posa)
if posa.is_lesson_posa_card? if posa.is_lesson_posa_card?
posa.user.is_a_student = true
# Associate user with guitar center school # Associate user with guitar center school
if posa.retailer.is_guitar_center? if posa.retailer && posa.retailer.is_guitar_center?
posa.user.is_a_student = true
if posa.user.school_id.nil? if posa.user.school_id.nil?
posa.user.school_id = School.guitar_center.id posa.user.school_id = School.guitar_center.id
end end
posa.user.save posa.user.save
end end
end end

View File

@ -9,7 +9,7 @@ module JamRuby
@@log = Logging.logger[LessonBooking] @@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_REQUESTED = 'requested'
STATUS_CANCELED = 'canceled' STATUS_CANCELED = 'canceled'
@ -97,7 +97,7 @@ module JamRuby
end end
def after_create 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 send_notices
end end
end end
@ -188,7 +188,7 @@ module JamRuby
success = self.save success = self.save
if !success if !success
puts "unable to accept lesson booking #{errors.inspect}" #puts "unable to accept lesson booking #{errors.inspect}"
end end
success success
end end
@ -467,6 +467,11 @@ module JamRuby
def resolved_test_drive_package def resolved_test_drive_package
result = nil 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 purchase = student.most_recent_test_drive_purchase
if purchase if purchase
# for lessons already packaged # for lessons already packaged
@ -528,21 +533,22 @@ module JamRuby
end end
end end
def distribution_price_in_cents(target, education, split = nil, fee_rate = nil) def distribution_price_in_cents(target, education, split = nil)
distribution = teacher_distribution_price_in_cents(target)
if split if split
distribution = teacher_distribution_price_in_cents(target, split)
(distribution * split).round (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 elsif education
distribution = teacher_distribution_price_in_cents(target, 0.0625)
(distribution * 0.0625).round # 0.0625 is 1/4th of 25% (distribution * 0.0625).round # 0.0625 is 1/4th of 25%
else else
distribution = teacher_distribution_price_in_cents(target)
distribution distribution
end end
end end
def teacher_distribution_price_in_cents(target) def teacher_distribution_price_in_cents(target, split = nil)
if is_single_free? if is_single_free?
0 0
elsif is_test_drive? 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 # we are in the month being billed. we should set the start date based on today
start_date = today start_date = today
end 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 else
booked_price * 100 booked_price * 100
end end
end 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? def is_single_free?
lesson_type == LESSON_TYPE_FREE lesson_type == LESSON_TYPE_FREE
end end
@ -674,6 +743,9 @@ module JamRuby
def card_approved def card_approved
self.card_presumed_ok = true self.card_presumed_ok = true
if posa_card_id
self.posa_card_purchased = true
end
if self.save && !sent_notices if self.save && !sent_notices
send_notices send_notices
end end
@ -792,7 +864,10 @@ module JamRuby
if lesson_type == LESSON_TYPE_TEST_DRIVE if lesson_type == LESSON_TYPE_TEST_DRIVE
# if the user has any jamclass credits, then we should get their most recent posa purchase # if the user has any jamclass credits, then we should get their most recent posa purchase
if user.jamclass_credits > 0 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 else
# otherwise, it's a normal test drive, and we should honor test_drive_package_choice if specified # 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 lesson_booking.test_drive_package_choice = test_drive_package_choice
@ -837,7 +912,7 @@ module JamRuby
end end
def self.unprocessed(current_user) 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 end
def self.requested(current_user) 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 # 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 def self.hourly_check
schedule_upcoming_lessons 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 bill_monthlies
end end
def self.bill_monthlies def self.bill_monthlies
now = Time.now now = Time.now
billable_monthlies(now).each do |lesson_booking| billable_monthlies(now).each do |lesson_booking|

View File

@ -29,14 +29,24 @@ module JamRuby
after_create :add_test_drives after_create :add_test_drives
after_create :create_charge after_create :create_charge
def validate_test_drive def validate_test_drive
if user if user
# if this is a posa card purchase, we won't stop it from getting created # if this is a posa card purchase, we won't stop it from getting created
if posa_card_id if posa_card
return return
end 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 end
end end
@ -61,7 +71,7 @@ module JamRuby
end end
def add_test_drives def add_test_drives
if posa_card_id if posa_card
#user.jamclass_credits incremented in posa_card.rb #user.jamclass_credits incremented in posa_card.rb
return return
end end
@ -85,12 +95,28 @@ module JamRuby
lesson_payment_charge.amount_in_cents / 100.0 lesson_payment_charge.amount_in_cents / 100.0
end 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) def self.create(user, lesson_booking, lesson_package_type, year = nil, month = nil, posa_card = nil)
purchase = LessonPackagePurchase.new purchase = LessonPackagePurchase.new
purchase.user = user purchase.user = user
purchase.lesson_booking = lesson_booking purchase.lesson_booking = lesson_booking
purchase.teacher = lesson_booking.teacher if lesson_booking purchase.teacher = lesson_booking.teacher if lesson_booking
purchase.posa_card = posa_card 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 if year
purchase.year = year purchase.year = year
@ -117,6 +143,7 @@ module JamRuby
purchase.teacher_distributions << teacher_dist purchase.teacher_distributions << teacher_dist
# price should always match the teacher_distribution, if there is one # price should always match the teacher_distribution, if there is one
purchase.price = teacher_dist.amount_in_cents / 100 purchase.price = teacher_dist.amount_in_cents / 100
purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
end end
if retailer_split && retailer_split > 0 if retailer_split && retailer_split > 0
@ -124,33 +151,39 @@ module JamRuby
teacher_dist.retailer = teacher.teacher.retailer teacher_dist.retailer = teacher.teacher.retailer
teacher_dist.save teacher_dist.save
purchase.teacher_distributions << teacher_dist purchase.teacher_distributions << teacher_dist
purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
end end
else else
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, false) teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, false)
purchase.teacher_distributions << teacher_dist 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 # price should always match the teacher_distribution, if there is one
purchase.price = teacher_dist.amount_in_cents / 100 purchase.price = teacher_dist.amount_in_cents / 100
if lesson_booking.school_on_school_payment? && lesson_booking.school.education if lesson_booking.school_on_school_payment? && lesson_booking.school.education
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, true) teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, true)
purchase.teacher_distributions << teacher_dist purchase.teacher_distributions << teacher_dist
purchase.reduced_roll_forward_amount_in_cents += teacher_dist.reduced_roll_forward_amount_in_cents
end end
end end
# record expected times for times played in the month
purchase.expected_session_times = lesson_booking.expected_session_times
end end
else else
purchase.recurring = false purchase.recurring = false
end end
if lesson_booking if lesson_booking
purchase.lesson_package_type = lesson_package_type ? lesson_package_type : lesson_booking.lesson_package_type 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? purchase.price = lesson_booking.booked_price if purchase.price.nil?
else else
purchase.lesson_package_type = lesson_package_type purchase.lesson_package_type = lesson_package_type
purchase.price = lesson_package_type.price if purchase.price.nil? purchase.price = lesson_package_type.price if purchase.price.nil?
end end
purchase.save purchase.save
purchase purchase
end end

View File

@ -6,11 +6,16 @@ module JamRuby
PRODUCT_TYPE = 'LessonPackageType' PRODUCT_TYPE = 'LessonPackageType'
TEST_DRIVE_4_ID = 'test-drive'
SINGLE_FREE = 'single-free' SINGLE_FREE = 'single-free'
TEST_DRIVE_4 = 'test-drive' TEST_DRIVE_4 = 'test-drive-4'
TEST_DRIVE_2 = 'test-drive-2' TEST_DRIVE_2 = 'test-drive-2'
TEST_DRIVE_1 = 'test-drive-1' TEST_DRIVE_1 = 'test-drive-1'
SINGLE = 'single' 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 = 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 :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 :name, presence: true
validates :description, presence: true validates :description, presence: true
validates :price, presence: true validates :price, presence: true
validates :package_type, presence: true, inclusion: {in: LESSON_PACKAGE_TYPES} validates :package_type, presence: true, inclusion: {in: LESSON_PACKAGE_TYPES}
def self.test_drive_package_ids 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 end
def self.monthly def self.monthly
LessonPackageType.find(MONTHLY) LessonPackageType.find(MONTHLY)
end end
@ -38,8 +47,20 @@ module JamRuby
LessonPackageType.find(SINGLE_FREE) LessonPackageType.find(SINGLE_FREE)
end 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 def self.test_drive_4
LessonPackageType.find(TEST_DRIVE_4) LessonPackageType.find(TEST_DRIVE_4_ID)
end end
def self.test_drive_2 def self.test_drive_2
@ -54,6 +75,7 @@ module JamRuby
LessonPackageType.find(SINGLE) LessonPackageType.find(SINGLE)
end end
def booked_price(lesson_booking) def booked_price(lesson_booking)
if is_single_free? if is_single_free?
0 0
@ -94,7 +116,7 @@ module JamRuby
end end
def is_test_drive? def is_test_drive?
id.start_with?('test-drive') is_test_drive
end end
def is_normal? def is_normal?
@ -112,7 +134,7 @@ module JamRuby
def plan_code def plan_code
if package_type == SINGLE_FREE if package_type == SINGLE_FREE
"lesson-package-single-free" "lesson-package-single-free"
elsif package_type == 'test-drive-4' elsif package_type == TEST_DRIVE_4
"lesson-package-test-drive-4" "lesson-package-test-drive-4"
elsif package_type == TEST_DRIVE_2 elsif package_type == TEST_DRIVE_2
"lesson-package-test-drive-2" "lesson-package-test-drive-2"

View File

@ -11,7 +11,7 @@ module JamRuby
@@log = Logging.logger[LessonSession] @@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 :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 delegate :pretty_scheduled_start, to: :music_session
@ -712,7 +712,7 @@ module JamRuby
end end
# only show 'fully booked lessons'; not those they can not possibly be paid for # 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 current_page = params[:page].nil? ? 1 : params[:page].to_i
next_page = current_page + 1 next_page = current_page + 1

View File

@ -6,6 +6,7 @@ module JamRuby
raise "lesson_booking is not monthly paid #{lesson_booking.admin_url}" if !lesson_booking.is_monthly_payment? 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) data = lesson_booking.predicted_times_for_month(start_day.year, start_day.month)
times = data[:times] times = data[:times]
@ -33,7 +34,53 @@ module JamRuby
result = lesson_booking.booked_price result = lesson_booking.booked_price
end 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 end
end end

View File

@ -126,7 +126,6 @@ module JamRuby
def refresh_stream(user, broadcast) def refresh_stream(user, broadcast)
stream_data = get_livestream(user) stream_data = get_livestream(user)
puts "REFRESH STREAM #{stream_data}"
broadcast.stream_id = stream_data["id"] broadcast.stream_id = stream_data["id"]
broadcast.stream_status = stream_data["status"]["streamStatus"] broadcast.stream_status = stream_data["status"]["streamStatus"]
broadcast.stream_name = stream_data["cdn"]["ingestionInfo"]["streamName"] broadcast.stream_name = stream_data["cdn"]["ingestionInfo"]["streamName"]
@ -185,7 +184,6 @@ module JamRuby
if livestream if livestream
# if livestream["status"]["streamStatus"] == "active" # if livestream["status"]["streamStatus"] == "active"
puts "LI EVESATREMA STATATUESNUHOENTUHENOSTHU #{livestream["status"]["streamStatus"]}"
transition_broadcast(user, broadcast, 'live', google_client) transition_broadcast(user, broadcast, 'live', google_client)
# end # end
else else
@ -1254,6 +1252,10 @@ SQL
sessions sessions
end end
def admin_url
APP_CONFIG.admin_root_url + "/admin/music_sessions/" + id
end
private private
def generate_share_token def generate_share_token

View File

@ -7,20 +7,25 @@ module JamRuby
JAM_TRACKS_5 = 'jam_tracks_5' JAM_TRACKS_5 = 'jam_tracks_5'
JAM_TRACKS_10 = 'jam_tracks_10' JAM_TRACKS_10 = 'jam_tracks_10'
JAM_CLASS_4 = 'jam_class_4' JAM_CLASS_4 = 'jam_class_4'
JAM_CLASS_2 = 'jam_class_2'
CARD_TYPES = CARD_TYPES =
[ [
JAM_TRACKS_5, JAM_TRACKS_5,
JAM_TRACKS_10, JAM_TRACKS_10,
JAM_CLASS_4 JAM_CLASS_4,
JAM_CLASS_2
] ]
belongs_to :user, class_name: "JamRuby::User" belongs_to :user, class_name: "JamRuby::User"
belongs_to :retailer, class_name: "JamRuby::Retailer" 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_many :posa_card_purchases, class_name: 'JamRuby::PosaCardPurchase'
has_one :lesson_package_purchase, class_name: 'JamRuby::LessonPackagePurchase' has_one :lesson_package_purchase, class_name: 'JamRuby::LessonPackagePurchase'
has_one :jam_track_right, class_name: "JamRuby::JamTrackRight" has_one :jam_track_right, class_name: "JamRuby::JamTrackRight"
validates :card_type, presence: true, inclusion: {in: CARD_TYPES} validates :card_type, presence: true, inclusion: {in: CARD_TYPES}
validates :code, presence: true, uniqueness: true validates :code, presence: true, uniqueness: true
@ -34,20 +39,9 @@ module JamRuby
validate :within_one_year validate :within_one_year
def is_lesson_posa_card? def is_lesson_posa_card?
card_type == JAM_CLASS_4 self.is_lesson
end 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 def already_activated
if activated_at && activated_at_was && activated_at_changed? if activated_at && activated_at_was && activated_at_changed?
@ -60,7 +54,7 @@ module JamRuby
end end
def within_one_year 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 if !user.can_claim_posa_card
self.errors.add(:claimed_at, 'was within 1 year') self.errors.add(:claimed_at, 'was within 1 year')
end end
@ -86,7 +80,7 @@ module JamRuby
end end
def must_be_activated def must_be_activated
if claimed_at && !activated_at if claimed_at && !preactivate && !activated_at
self.errors.add(:activated_at, 'must already be set') self.errors.add(:activated_at, 'must already be set')
end end
end end
@ -94,11 +88,11 @@ module JamRuby
def check_attributed def check_attributed
if user && user_id_changed? if user && user_id_changed?
if card_type == JAM_TRACKS_5 if card_type == JAM_TRACKS_5
user.gifted_jamtracks += 5 user.gifted_jamtracks += credits
elsif card_type == JAM_TRACKS_10 elsif card_type == JAM_TRACKS_10
user.gifted_jamtracks += 10 user.gifted_jamtracks += credits
elsif card_type == JAM_CLASS_4 elsif is_lesson_posa_card?
user.jamclass_credits += 4 user.jamclass_credits += credits
else else
raise "unknown card type #{card_type}" raise "unknown card type #{card_type}"
end end
@ -106,18 +100,6 @@ module JamRuby
end end
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 def product_info
price = nil price = nil
plan_code = nil plan_code = nil
@ -128,9 +110,9 @@ module JamRuby
elsif card_type == JAM_TRACKS_10 elsif card_type == JAM_TRACKS_10
price = 19.99 price = 19.99
plan_code = 'posa-jatracks-10' plan_code = 'posa-jatracks-10'
elsif card_type == JAM_CLASS_4 elsif is_lesson_posa_card?
price = 49.99 price = lesson_package_type.price
plan_code = 'posa-jamclass-4' plan_code = "posa-jamclass-#{credits}"
else else
raise "unknown card type #{card_type}" raise "unknown card type #{card_type}"
end end
@ -148,20 +130,53 @@ module JamRuby
self.save self.save
end 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) def claim(user)
self.user = user self.user = user
self.claimed_at = Time.now self.claimed_at = Time.now
if self.save if self.save
if user.errors.any?
# happens on signup if bad email etc
return
end
UserWhitelist.card_create(user, 'posa') 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 # when you claim a POSA card, you are also making a LessonPackagePurchase
if is_lesson_posa_card? if is_lesson_posa_card?
GuitarCenter.post_posa_claim(self) 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 end
end end
@ -171,8 +186,8 @@ module JamRuby
'JT-5' 'JT-5'
elsif card_type == JAM_TRACKS_10 elsif card_type == JAM_TRACKS_10
'JT-10' 'JT-10'
elsif card_type == JAM_CLASS_4 elsif is_lesson_posa_card?
'JC-4' "JC-#{credits}"
else else
raise "unknown card type #{card_type}" raise "unknown card type #{card_type}"
end end

View File

@ -233,8 +233,8 @@ module JamRuby
free && non_free free && non_free
end end
def self.purchase_test_drive(current_user, lesson_package_type, booking = nil) 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) self.purchase_lesson(nil, current_user, booking, lesson_package_type, nil, nil, false, posa_card)
end end
def self.post_sale_test_failure def self.post_sale_test_failure
@ -261,7 +261,7 @@ module JamRuby
end end
# this is easy to make generic, but right now, it just purchases lessons # 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 stripe_charge = nil
sale = nil sale = nil
purchase = 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) 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 post_sale_test_failure
@ -310,7 +310,7 @@ module JamRuby
{sale: sale, stripe_charge: stripe_charge, purchase: purchase} {sale: sale, stripe_charge: stripe_charge, purchase: purchase}
end 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 if lesson_package_purchase
target = lesson_package_purchase target = lesson_package_purchase
elsif lesson_session elsif lesson_session
@ -322,10 +322,9 @@ module JamRuby
current_user.sync_stripe_customer current_user.sync_stripe_customer
purchase = lesson_package_purchase 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? if purchase.errors.any?
puts "purchase errors #{purchase.errors.inspect}"
price_info = {} price_info = {}
price_info[:purchase] = purchase price_info[:purchase] = purchase
return price_info return price_info

View File

@ -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) 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 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") 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 # order in this way: https://jamkazam.atlassian.net/browse/VRFS-4058
@ -349,8 +349,8 @@ module JamRuby
reviews.order('created_at desc').limit(20) reviews.order('created_at desc').limit(20)
end end
def mark_background_checked def mark_background_checked(time)
self.background_check_at = Time.now self.background_check_at = time
self.save! self.save!
end end

View File

@ -55,14 +55,14 @@ module JamRuby
end end
def self.create_for_lesson_package_purchase(lesson_package_purchase, for_education, split = nil, fee_rate = nil) 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 = create(lesson_package_purchase, for_education, split, fee_rate)
distribution.lesson_package_purchase = lesson_package_purchase distribution.lesson_package_purchase = lesson_package_purchase
distribution.education = for_education distribution.education = for_education
# lock down the teacher_fee_in_cents # lock down the teacher_fee_in_cents
distribution.teacher_fee_in_cents = distribution.calculate_teacher_fee(split, fee_rate) 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 distribution
end end
@ -71,7 +71,7 @@ module JamRuby
distribution.teacher = target.teacher distribution.teacher = target.teacher
distribution.ready = false distribution.ready = false
distribution.distributed = 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.school = target.lesson_booking.school
distribution distribution
end end

View File

@ -37,7 +37,7 @@ module JamRuby
pending_teacher_payments.each do |row| pending_teacher_payments.each do |row|
teacher = User.find(row['id']) 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) payment = TeacherPayment.charge(teacher)
if payment.nil? || !payment.teacher_payment_charge.billed if payment.nil? || !payment.teacher_payment_charge.billed
break break
@ -62,6 +62,7 @@ module JamRuby
def last_billed_at_date def last_billed_at_date
teacher_payment_charge.last_billed_at_date teacher_payment_charge.last_billed_at_date
end end
def charge_retry_hours def charge_retry_hours
24 24
end end
@ -82,7 +83,7 @@ module JamRuby
end end
if payment.teacher_distribution.nil? 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? if teacher_distribution.nil?
return return
end end
@ -90,34 +91,45 @@ module JamRuby
end end
payment.school = payment.teacher_distribution.school 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 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? effective_in_cents = payment.real_distribution_in_cents
charge = TeacherPaymentCharge.new
charge.user = payment.payable_teacher if payment.teacher_payment_charge.nil?
charge.amount_in_cents = (effective_in_cents / (1 - APP_CONFIG.stripe[:ach_pct])).round charge = TeacherPaymentCharge.new
charge.fee_in_cents = payment.fee_in_cents charge.user = payment.payable_teacher
charge.teacher_payment = payment charge.amount_in_cents = (effective_in_cents / (1 - APP_CONFIG.stripe[:ach_pct])).round
payment.teacher_payment_charge = charge charge.fee_in_cents = payment.fee_in_cents
# charge.save! 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 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! # 0 amount distribution; these occur in 100% roll forward scenarios (previous month was completely missed)
payment.save!
payment.teacher_payment_charge.charge
if payment.teacher_payment_charge.billed
payment.teacher_distribution.distributed = true payment.teacher_distribution.distributed = true
payment.teacher_distribution.save! payment.teacher_distribution.save!
end end
payment payment
end end
end end

View File

@ -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 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 # 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' 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 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_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 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" # 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 # 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' 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_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 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 :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 # Shopping carts
has_many :shopping_carts, :class_name => "JamRuby::ShoppingCart" has_many :shopping_carts, :class_name => "JamRuby::ShoppingCart"
@ -477,7 +477,7 @@ module JamRuby
def age def age
now = Time.now.utc.to_date 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 end
def session_count def session_count
@ -685,6 +685,8 @@ module JamRuby
id id
end end
def set_password(old_password, new_password, new_password_confirmation) def set_password(old_password, new_password, new_password_confirmation)
# so that UserObserver knows to send a confirmation email on success # so that UserObserver knows to send a confirmation email on success
@ -720,7 +722,7 @@ module JamRuby
end end
def change_password(new_password, new_password_confirmation) 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. # verification step.
self.updating_password = true self.updating_password = true
self.password = new_password 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 a gift card value was passed in, then try to find that gift card and apply it to user
if gift_card if gift_card
# first try posa card # first try posa card
posa_card = PosaCard.where(code: gift_card).first posa_card = PosaCard.where(code: gift_card).first
if posa_card if posa_card
posa_card.claim(user) posa_card.claim(user)
user.posa_cards << posa_card 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 else
user.expecting_gift_card = true user.expecting_gift_card = true
found_gift_card = GiftCard.where(code: gift_card).where(user_id: nil).first 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
end end
@ -1448,6 +1454,17 @@ module JamRuby
#if school && school.education #if school && school.education
# UserMailer.student_education_welcome_message(user).deliver_now # UserMailer.student_education_welcome_message(user).deliver_now
#else #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 UserMailer.student_welcome_message(user).deliver_now
#end #end
elsif user.is_a_teacher elsif user.is_a_teacher
@ -1975,6 +1992,10 @@ module JamRuby
APP_CONFIG.admin_root_url + "/admin/users/" + id APP_CONFIG.admin_root_url + "/admin/users/" + id
end 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 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" APP_CONFIG.admin_root_url + "/admin/jam_track_rights?q[user_id_equals]=#{id}&commit=Filter&order=created_at DESC"
end end
@ -2042,7 +2063,7 @@ module JamRuby
# validate if within waiting period # validate if within waiting period
def can_claim_posa_card 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 end
def lessons_with_teacher(teacher) def lessons_with_teacher(teacher)
@ -2197,6 +2218,7 @@ module JamRuby
lesson_package_type = nil lesson_package_type = nil
uncollectables = nil uncollectables = nil
choice = nil choice = nil
posa_card = nil
User.transaction do User.transaction do
if params[:name].present? if params[:name].present?
@ -2209,20 +2231,29 @@ module JamRuby
if params[:test_drive] if params[:test_drive]
self.reload self.reload
if booking if booking
# bookin will indicate test package
lesson_package_type = booking.resolved_test_drive_package lesson_package_type = booking.resolved_test_drive_package
posa_card = booking.posa_card
elsif choice elsif choice
# packages also indicate lesson package
lesson_package_type = choice.lesson_package_type 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 end
if lesson_package_type.nil? result = Sale.purchase_test_drive(self, lesson_package_type, booking, posa_card)
lesson_package_type = LessonPackageType.test_drive_4
end
result = Sale.purchase_test_drive(self, lesson_package_type, booking)
test_drive = result[:sale] test_drive = result[:sale]
purchase = result[:purchase] purchase = result[:purchase]
if posa_card && !purchase.errors.any?
posa_card.has_been_purchased(false)
end
if booking && !purchase.errors.any? if booking && !purchase.errors.any?
# the booking would not have a lesson_package_purchase associated yet, so let's associate it # 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) booking.lesson_sessions.update_all(lesson_package_purchase_id: purchase.id)
@ -2233,6 +2264,7 @@ module JamRuby
end end
{lesson: booking, test_drive: test_drive, purchase: purchase, lesson_package_type: lesson_package_type, uncollectables: uncollectables, package: choice} {lesson: booking, test_drive: test_drive, purchase: purchase, lesson_package_type: lesson_package_type, uncollectables: uncollectables, package: choice}
end 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 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 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 def most_recent_test_drive_purchase
lesson_purchases.where('lesson_package_type_id in (?)', LessonPackageType.test_drive_package_ids).order('created_at desc').first lesson_purchases.where('lesson_package_type_id in (?)', LessonPackageType.test_drive_package_ids).order('created_at desc').first
end end

View File

@ -12,6 +12,7 @@ module JamRuby
LessonSession.hourly_check LessonSession.hourly_check
TeacherPayment.hourly_check TeacherPayment.hourly_check
@@log.debug("done") @@log.debug("done")
end end
end end

View File

@ -901,7 +901,24 @@ FactoryGirl.define do
factory :posa_card, class: 'JamRuby::PosaCard' do factory :posa_card, class: 'JamRuby::PosaCard' do
sequence(:code) { |n| n.to_s } 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 end
factory :posa_card_type, class: 'JamRuby::PosaCardType' do factory :posa_card_type, class: 'JamRuby::PosaCardType' do

View File

@ -241,6 +241,47 @@ describe "Monthly Recurring Lesson Flow" do
user.reload user.reload
user.remaining_test_drives.should eql 0 user.remaining_test_drives.should eql 0
UserMailer.deliveries.length.should eql 0 # one for student 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 end
it "works (school on school education)" do it "works (school on school education)" do

View File

@ -16,7 +16,7 @@ describe "TestDrive Lesson Flow" do
let(:affiliate_partner) { FactoryGirl.create(:affiliate_partner) } let(:affiliate_partner) { FactoryGirl.create(:affiliate_partner) }
let(:affiliate_partner2) { FactoryGirl.create(:affiliate_partner, lesson_rate: 0.30) } let(:affiliate_partner2) { FactoryGirl.create(:affiliate_partner, lesson_rate: 0.30) }
let(:school) { FactoryGirl.create(:school) } 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)} let(:retailer) {FactoryGirl.create(:retailer)}
before { before {
@ -271,6 +271,7 @@ describe "TestDrive Lesson Flow" do
booking.user.should eql user booking.user.should eql user
booking.sent_notices.should be_true booking.sent_notices.should be_true
booking.posa_card.should eql card_lessons booking.posa_card.should eql card_lessons
booking.posa_card_purchased.should be_true
user.unprocessed_test_drive.should be_nil user.unprocessed_test_drive.should be_nil
teacher_user.has_booked_test_drive_with_student?(user).should be_true teacher_user.has_booked_test_drive_with_student?(user).should be_true

View File

@ -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

View File

@ -26,7 +26,9 @@ describe LessonSessionMonthlyPrice do
booking.booked_price.should eql 30.00 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 end
it "middle of the month" do it "middle of the month" do
@ -38,7 +40,10 @@ describe LessonSessionMonthlyPrice do
booking.booked_price.should eql 30.00 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 end
it "end of the month which has a last billable day based on slot" do 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.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 end
it "end of the month which is not a last billable days" do 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.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 end
end end

View File

@ -5,7 +5,8 @@ describe PosaCard do
let(:user) {FactoryGirl.create(:user)} let(:user) {FactoryGirl.create(:user)}
let(:card) {FactoryGirl.create(:posa_card)} let(:card) {FactoryGirl.create(:posa_card)}
let(:card2) {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)} let(:retailer) {FactoryGirl.create(:retailer)}
it "created by factory" do it "created by factory" do
card.touch card.touch
@ -42,10 +43,12 @@ describe PosaCard do
end end
describe "claim" do describe "claim" do
it "succeeds" do it "succeeds simple" do
PosaCard.activate(card, retailer) PosaCard.activate(card, retailer)
card.reload card.reload
card.claim(user) card.claim(user)
user.reload
user.jamclass_credits.should be 0
card.errors.any?.should be false card.errors.any?.should be false
card.claimed_at.should_not be_nil card.claimed_at.should_not be_nil
@ -56,16 +59,33 @@ describe PosaCard do
PosaCard.activate(card_lessons, retailer) PosaCard.activate(card_lessons, retailer)
card_lessons.reload card_lessons.reload
card_lessons.claim(user) card_lessons.claim(user)
user.jamclass_credits.should be 4
card_lessons.errors.any?.should be false card_lessons.errors.any?.should be false
card_lessons.claimed_at.should_not be_nil card_lessons.claimed_at.should_not be_nil
card_lessons.user.should eql user card_lessons.user.should eql user
card_lessons.reload card_lessons.reload
card_lessons.lesson_package_purchase.should_not be_nil 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 card_lessons.lesson_package_purchase.posa_card.should eql card_lessons
end 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 it "associates student automatically for GC" do
gc = GuitarCenter.init gc = GuitarCenter.init
gc_owner = gc[:user] gc_owner = gc[:user]
@ -83,7 +103,7 @@ describe PosaCard do
card_lessons.user.should eql user card_lessons.user.should eql user
card_lessons.reload card_lessons.reload
card_lessons.lesson_package_purchase.should_not be_nil 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 card_lessons.lesson_package_purchase.posa_card.should eql card_lessons
end end
@ -120,16 +140,16 @@ describe PosaCard do
end end
it "can't be within one year" do it "can't be within one year" do
PosaCard.activate(card, retailer) PosaCard.activate(card_lessons, retailer)
card.reload card_lessons.reload
card.claim(user) card_lessons.claim(user)
PosaCard.activate(card2, retailer) PosaCard.activate(card_lessons_2, retailer)
card2.reload card_lessons_2.reload
card2.claim(user) card_lessons_2.claim(user)
card2.errors.any?.should be true card_lessons_2.errors.any?.should be true
card2.errors[:user].should eql ['was within 1 year'] card_lessons_2.errors[:claimed_at].should eql ['was within 1 year']
end end
end end
end end

View File

@ -889,6 +889,7 @@ describe Sale do
end end
it "can succeed with no booking; just intent" do it "can succeed with no booking; just intent" do
user.desired_package = LessonPackageType.test_drive_4
intent = TeacherIntent.create(user, teacher, 'book-test-drive') intent = TeacherIntent.create(user, teacher, 'book-test-drive')
token = create_stripe_token token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', test_drive: true}) result = user.payment_update({token: token, zip: '78759', test_drive: true})
@ -912,6 +913,7 @@ describe Sale do
end end
it "will reject second test drive purchase" do it "will reject second test drive purchase" do
user.desired_package = LessonPackageType.test_drive_4
intent = TeacherIntent.create(user, teacher, 'book-test-drive') intent = TeacherIntent.create(user, teacher, 'book-test-drive')
token = create_stripe_token token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', test_drive: true}) result = user.payment_update({token: token, zip: '78759', test_drive: true})

View File

@ -710,7 +710,7 @@ describe User do
user.age.should == 9 user.age.should == 9
user.birth_date = nil user.birth_date = nil
user.age.should == "" user.age.should == nil
end end
end end
@ -730,7 +730,7 @@ describe User do
end end
it "allow no_show aggregation" do it "allow no_show aggr egation" do
user.mod_merge({"no_show" => {"some_screen1" => true}}) user.mod_merge({"no_show" => {"some_screen1" => true}})
user.save! user.save!
user.reload user.reload

View File

@ -19,6 +19,10 @@ def app_config
'alerts@jamkazam.com' 'alerts@jamkazam.com'
end end
def email_jamclass_alerts_alias
'jamclass-alerts@jamkazam.com'
end
def email_crashes_alias def email_crashes_alias
'clientcrash@jamkazam.com' 'clientcrash@jamkazam.com'
end end

View File

@ -11,6 +11,10 @@
context.JK.Banner.showAlert( context.JK.Banner.showAlert(
{ title: "Close JamKazam Application", { title: "Close JamKazam Application",
buttons: [ buttons: [
{name: 'CANCEL SHUTDOWN', click: function() {
logger.debug("'CANCEL SHUTDOWN' selected")
context.JK.Banner.hide();
}},
{name: 'COMPLETELY SHUT DOWN THE APP', click: function() { {name: 'COMPLETELY SHUT DOWN THE APP', click: function() {
logger.debug("'COMPLETELY SHUT DOWN THE APP' selected") logger.debug("'COMPLETELY SHUT DOWN THE APP' selected")
context.jamClient.ShutdownApplication() context.jamClient.ShutdownApplication()

View File

@ -100,7 +100,7 @@ UserStore = context.UserStore
userDetailDone: (response) -> userDetailDone: (response) ->
if response.id == @state.teacherId 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 #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 else
logger.debug("BookLesson: ignoring teacher details", response.id, @state.teacherId) logger.debug("BookLesson: ignoring teacher details", response.id, @state.teacherId)
@ -235,7 +235,7 @@ UserStore = context.UserStore
@setState({updating: false}) @setState({updating: false})
UserActions.refresh() UserActions.refresh()
#if response.user['has_stored_credit_card?'] || @state.school_on_school || response.posa_card_id? #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.<br/><br/>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.") context.JK.Banner.showNotice("Lesson Requested","The teacher has been notified of your lesson request, and should respond soon.<br/><br/>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/lesson-booking/#{response.id}"
url = "/client#/jamclass" url = "/client#/jamclass"
@ -302,16 +302,17 @@ UserStore = context.UserStore
else else
for minutes in enabledMinutes for minutes in enabledMinutes
lesson_price = teacher["price_per_lesson_#{minutes}_cents"] lesson_price = teacher["price_per_lesson_#{minutes}_cents"]
value = "single|#{minutes}" if lesson_price
display = "#{minutes} Minute Lesson Each Week - $#{(lesson_price / 100).toFixed(2)} Per Week" value = "single|#{minutes}"
results.push(`<option key={value} value={value}>{display}</option>`) display = "#{minutes} Minute Lesson Each Week - $#{(lesson_price / 100).toFixed(2)} Per Week"
results.push(`<option key={value} value={value}>{display}</option>`)
for minutes in enabledMinutes for minutes in enabledMinutes
monthly_price = teacher["price_per_month_#{minutes}_cents"] monthly_price = teacher["price_per_month_#{minutes}_cents"]
value = "monthly|#{minutes}" if monthly_price
display = "#{minutes} Minute Lesson Each Week - $#{(monthly_price / 100).toFixed(2)} Per Month" value = "monthly|#{minutes}"
results.push(`<option value={value}>{display}</option>`) display = "#{minutes} Minute Lesson Each Week - $#{(monthly_price / 100).toFixed(2)} Per Month"
results.push(`<option value={value}>{display}</option>`)
if results.length == 0 if results.length == 0
results.push(`<option value=''>This teacher has no pricing options</option>`) results.push(`<option value=''>This teacher has no pricing options</option>`)
@ -348,8 +349,8 @@ UserStore = context.UserStore
am_pm = [`<option key="AM" value="AM">AM</option>`, `<option key="PM" value="PM">PM</option>`] am_pm = [`<option key="AM" value="AM">AM</option>`, `<option key="PM" value="PM">PM</option>`]
bookLessonClasses = classNames({"button-orange": true, 'book-lesson-btn': 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}) cancelClasses = classNames({"button-grey": true, disabled: !this.state.teacher? || @state.updating})
descriptionErrors = context.JK.reactSingleFieldErrors('description', @state.descriptionErrors) descriptionErrors = context.JK.reactSingleFieldErrors('description', @state.descriptionErrors)
bookedPriceErrors = context.JK.reactSingleFieldErrors('booked_price', @state.bookedPriceErrors) bookedPriceErrors = context.JK.reactSingleFieldErrors('booked_price', @state.bookedPriceErrors)

View File

@ -233,6 +233,11 @@ LessonTimerActions = context.LessonTimerActions
rescheduleLesson: (lesson) -> rescheduleLesson: (lesson) ->
if lesson.recurring if lesson.recurring
buttons = [] buttons = []
buttons.push({
name: 'CANCEL',
buttonStyle: 'button-grey',
click: (() => (logger.debug("cancelling out of reschedule dialog")))
})
buttons.push({ buttons.push({
name: 'THIS LESSON', name: 'THIS LESSON',
buttonStyle: 'button-orange', buttonStyle: 'button-orange',

View File

@ -39,7 +39,7 @@ UserStore = context.UserStore
return return
slot.slotTime = @slotTime(slot, booking) slot.slotTime = @slotTime(slot, booking)
slot.is_recurring = @isRecurring() slot.is_recurring = slot.slot_type == 'recurring'
if slot['is_teacher_created?'] if slot['is_teacher_created?']
slot.creatorRole = 'teacher' slot.creatorRole = 'teacher'
slot.creatorRoleRelative = 'teacher' slot.creatorRoleRelative = 'teacher'
@ -169,6 +169,7 @@ UserStore = context.UserStore
@focusedLesson()? @focusedLesson()?
focusedLesson: () -> focusedLesson: () ->
console.log("focusedlesos", this.state?.booking?.focused_lesson)
this.state?.booking?.focused_lesson this.state?.booking?.focused_lesson
updateBookingState: (booking) -> updateBookingState: (booking) ->
@ -425,10 +426,22 @@ UserStore = context.UserStore
false false
isApproved: () -> isApproved: () ->
@state.booking?.status == 'approved' if @hasFocusedLesson()
@focusedLesson().status == 'approved'
else
@state.booking?.status == 'approved'
isCanceled: () -> 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: () -> isSuspended: () ->
@state.booking?.status == 'suspended' @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)}." text = "Preferred day/time for lesson is #{this.slotTime(defaultSlot)}. Secondary option is #{this.slotTime(altSlot)}."
slotTime: (slot, booking = this.state.booking) -> slotTime: (slot, booking = this.state.booking) ->
if @hasFocusedLesson() || !@isRecurring(booking) if @hasFocusedLesson() || slot.slot_type == 'single' #!@isRecurring(booking)
slot.pretty_start_time slot.pretty_start_time
else else
"#{this.dayOfWeek(slot)} at #{this.dayTime(slot)}" "#{this.dayOfWeek(slot)} at #{this.dayTime(slot)}"
slotTimePhrase: (slot) -> slotTimePhrase: (slot) ->
if @isRecurring() console.log("SLOT", slot)
if slot.slot_type == 'recurring'
# if @isRecurring()
"each " + @slotTime(slot) "each " + @slotTime(slot)
else else
@slotTime(slot) @slotTime(slot)
@ -709,6 +724,10 @@ UserStore = context.UserStore
header = 'respond to lesson request' header = 'respond to lesson request'
content = @renderTeacherRequested() content = @renderTeacherRequested()
else if @isApproved()
header = @approvedHeader()
content = @renderTeacherApproved()
else if @isCounter() else if @isCounter()
if @isTeacherCountered() if @isTeacherCountered()
header = 'your proposed alternate day/time is still pending' header = 'your proposed alternate day/time is still pending'
@ -716,10 +735,6 @@ UserStore = context.UserStore
header = 'student has proposed an alternate day/time' header = 'student has proposed an alternate day/time'
content = @renderTeacherCountered() content = @renderTeacherCountered()
else if @isApproved()
header = @approvedHeader()
content = @renderTeacherApproved()
else if @isCompleted() else if @isCompleted()
header = @completedHeader() header = @completedHeader()
content = @renderTeacherComplete() content = @renderTeacherComplete()
@ -750,6 +765,11 @@ UserStore = context.UserStore
header = 'your lesson has been requested' header = 'your lesson has been requested'
content = @renderStudentRequested() content = @renderStudentRequested()
else if @isApproved()
header = @approvedHeader()
content = @renderStudentApproved()
else if @isCounter() else if @isCounter()
if @isTeacherCountered() if @isTeacherCountered()
header = 'teacher has proposed an alternate day/time' header = 'teacher has proposed an alternate day/time'
@ -757,10 +777,6 @@ UserStore = context.UserStore
header = 'your proposed alternate day/time is still pending' header = 'your proposed alternate day/time is still pending'
content = @renderTeacherCountered() content = @renderTeacherCountered()
else if @isApproved()
header = @approvedHeader()
content = @renderStudentApproved()
else if @isCompleted() else if @isCompleted()
header = @completedHeader() header = @completedHeader()
content = @renderStudentComplete() content = @renderStudentComplete()

View File

@ -251,7 +251,7 @@
proposeAltLabelText = "Propose alternate day/time" proposeAltLabelText = "Propose alternate day/time"
for slot, i in @props.slots for slot, i in @props.slots
if this.props.is_recurring if slot.is_recurring
slotDetail = `<div className="slot-detail">Each {slot.slotTime}</div>` slotDetail = `<div className="slot-detail">Each {slot.slotTime}</div>`
else else
slotDetail = `<div className="slot-detail">{slot.slotTime}</div>` slotDetail = `<div className="slot-detail">{slot.slotTime}</div>`

View File

@ -307,47 +307,46 @@ UserStore = context.UserStore
if response.test_drive? if response.test_drive?
# ok, they bought a package # ok, they bought a package
if response.lesson_package_type? 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) logger.debug("testDriveCount: " + testDriveCount)
testDriveCountInt = parseInt(testDriveCount); context.JK.GA.trackTestDrivePurchase(testDriveCount);
if context._.isNaN(testDriveCountInt)
testDriveCountInt = 3
context.JK.GA.trackTestDrivePurchase(testDriveCountInt); if response.package?
logger.debug("packaged TD purchase...")
if response.test_drive?.teacher_id # the user bought a package that already includes the 1, 2, or 4 teachers as part of it
teacher_id = response.test_drive.teacher_id if testDriveCount == 1
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." 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." text = "Each teacher has received your request and should respond shortly."
else location = "/client#/jamclass"
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." 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" location = "/client#/jamclass"
else else if response.teacher?.id
if @state.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
# the user bought the testdrive, and there is a teacher of interest in context (but no booking) teacher_id = response.teacher.id
if testDriveCount == '1' if testDriveCount == '1'
text = "You now have 1 TestDrive credit.<br/><br/>We've taken you to the lesson booking screen for the teacher you initially showed interest in." text = "You now have 1 TestDrive credit.<br/><br/>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 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.<br/><br/>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 else
# the user bought test drive, but 'cold' , i.e., no teacher in context text = "You now have #{testDriveCount} TestDrive credits that you can take with #{testDriveCount} different teachers.<br/><br/>We've taken you to the lesson booking screen for the teacher you initially showed interest in."
if testDriveCount == '1' location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
text = "You now have 1 TestDrive credit.<br/><br/>We've taken you to the Teacher Search screen, so you can search for teachers right for you." else
location = "/client#/teachers/search" logger.debug("straight TD package")
else # the user bought test drive, but 'cold' , and, no teacher in context
text = "You now have #{testDriveCount} TestDrive credits that you can take with #{testDriveCount} different teachers.<br/><br/>We've taken you to the Teacher Search screen, so you can search for teachers right for you." if testDriveCount == 1
location = "/client#/teachers/search" text = "You now have 1 TestDrive credit.<br/><br/>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.<br/><br/>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) context.JK.Banner.showNotice("TestDrive Purchased",text)
window.location = location window.location = location
@ -495,14 +494,24 @@ UserStore = context.UserStore
else else
alert("unknown package type") alert("unknown package type")
else 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 = `<span>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.</span>` explanation = `<span>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.</span>`
else if this.state.user.lesson_package_type_id == 'test-drive-1' else if lesson_package_type_id == 'test-drive-1'
explanation =`<span>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.</span>` explanation =`<span>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.</span>`
else if this.state.user.lesson_package_type_id == 'test-drive-2' else if lesson_package_type_id == 'test-drive-2'
explanation =`<span>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.</span>` explanation =`<span>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.</span>`
else if lesson_package_type_id == 'amazon-test-drive-paid-4'
explanation = `<span>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.</span>`
else else
alert("You do not have a test drive package selected: " + this.state.user.lesson_package_type_id ) explanation = `<span>Loading your TestDrive packaging info...</span>`
#alert("You do not have a test drive package selected: " + this.state.user.lesson_package_type_id )
bookingDetail = `<p>{explanation} bookingDetail = `<p>{explanation}
@ -531,7 +540,7 @@ UserStore = context.UserStore
</p>` </p>`
else if this.state.lesson.payment_style == 'weekly' else if this.state.lesson.payment_style == 'weekly'
bookingInfo = `<p>You are booking a weekly recurring series of {lesson_length}-minute bookingInfo = `<p>You are booking a weekly recurring series of {lesson_length}-minute
lessons, to be paid individually as each lesson is taken, until cancelled.</p>` lessons for ${this.bookedPrice()}, to be paid individually as each lesson is taken, until cancelled.</p>`
bookingDetail = `<p> bookingDetail = `<p>
Your card will be charged on the day of each lesson. If you need to cancel a lesson, you must do so at 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. 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
</p>` </p>`
else if this.state.lesson.payment_style == 'monthly' else if this.state.lesson.payment_style == 'monthly'
bookingInfo = `<p>You are booking a weekly recurring series of {lesson_length}-minute bookingInfo = `<p>You are booking a weekly recurring series of {lesson_length}-minute
lessons, to be paid for monthly until cancelled.</p>` lessons for ${this.bookedPrice()}, to be paid for monthly until cancelled.</p>`
bookingDetail = `<p> bookingDetail = `<p>
Your card will be charged on the first day of each month. Canceling individual lessons does not earn a 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 refund when buying monthly. To cancel, you must cancel at least 24 hours before the beginning of the

View File

@ -81,7 +81,7 @@ proficiencyDescriptionMap = {
# mount it # mount it
@profileClipboard = new Clipboard($profileLink.get(0), { @profileClipboard = new Clipboard($profileLink.get(0), {
text: => 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? else if $profileLink.length == 0 && @profileClipboard?
@profileClipboard.destroy() @profileClipboard.destroy()
@ -245,10 +245,15 @@ proficiencyDescriptionMap = {
biography = biography.replace(/\n/g, "<br/>") biography = biography.replace(/\n/g, "<br/>")
`<div className="section bio"> # make button available only if this is the current user viewing
<a className="copy-profile-link button-orange" href='#' onClick={this.copyProfileLink}> if @state.userId == @user?.id
copyUrlToClipboard = `<a className="copy-profile-link button-orange" href='#' onClick={this.copyProfileLink}>
COPY PROFILE URL TO CLIPBOARD COPY PROFILE URL TO CLIPBOARD
</a><h3>Teacher Profile {this.editProfileLink('edit profile', 'introduction')}</h3> </a>`
`<div className="section bio">
{copyUrlToClipboard}<h3>Teacher Profile {this.editProfileLink('edit profile', 'introduction')}</h3>
<div className="section-content"> <div className="section-content">
<div dangerouslySetInnerHTML={{__html: biography}}></div> <div dangerouslySetInnerHTML={{__html: biography}}></div>
</div> </div>
@ -696,6 +701,17 @@ proficiencyDescriptionMap = {
else else
age = null age = null
backgroundCheck = null
backgroundCheckAt = @state.user?.teacher?.background_check_at
if backgroundCheckAt
backgroundCheck =
`<div className="backgroundCheck">
<div>Background Check:</div>
<div className="background-check-time">{context.JK.formatDateShort(new Date(backgroundCheckAt))} <img src="/assets/content/background-check.png" /></div>
</div>`
`<div className="profile-about-left"> `<div className="profile-about-left">
<div className="left-content"> <div className="left-content">
<div className="location"> <div className="location">
@ -709,6 +725,8 @@ proficiencyDescriptionMap = {
<div className="last-signed-in">Last Signed In:</div> <div className="last-signed-in">Last Signed In:</div>
<div>{"very recently"}</div> <div>{"very recently"}</div>
</div> </div>
{backgroundCheck}
</div> </div>
</div>` </div>`

View File

@ -137,10 +137,10 @@ ProfileActions = @ProfileActions
if @state.next == null if @state.next == null
@contentBodyScroller.off('scroll') @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() @endOfList.text('No Teachers found matching your search').show()
logger.debug("TeacherSearch: empty search") logger.debug("TeacherSearch: empty search")
else if @state.currentPage > 0 else if @state.currentPage > 0 && !@state.searching
logger.debug("end of search") logger.debug("end of search")
@endOfList.text('No more Teachers').show() @endOfList.text('No more Teachers').show()
else else
@ -238,13 +238,16 @@ ProfileActions = @ProfileActions
bookSingleBtn = null bookSingleBtn = null
bookTestDriveBtn = null bookTestDriveBtn = null
backgroundCheck = null
if user.teacher.background_check_at
backgroundCheck = `<img className="background-check" src="/assets/content/background-check.png" />`
if !school_on_school && (!@state.user? || @state.user.jamclass_credits > 0 || @state.user.remaining_test_drives > 0 || @state.user['can_buy_test_drive?']) if !school_on_school && (!@state.user? || @state.user.jamclass_credits > 0 || @state.user.remaining_test_drives > 0 || @state.user['can_buy_test_drive?'])
bookTestDriveBtn = `<a className="button-orange try-test-drive" onClick={this.bookTestDrive.bind(this, user)}>BOOK TESTDRIVE LESSON</a>` bookTestDriveBtn = `<a className="button-orange try-test-drive" onClick={this.bookTestDrive.bind(this, user)}>BOOK TESTDRIVE LESSON</a>`
else else
bookSingleBtn = `<a className="button-orange try-normal" onClick={this.bookNormalLesson.bind(this, user)}>BOOK LESSON</a>` bookSingleBtn = `<a className="button-orange try-normal" onClick={this.bookNormalLesson.bind(this, user)}>BOOK LESSON</a>`
resultsJsx.push(`<div key={user.id} className="teacher-search-result" data-teacher-id={user.id}> resultsJsx.push(`<div key={user.id} className="teacher-search-result" data-teacher-id={user.id}>
<div className="user-avatar"> <div className="user-avatar">
{backgroundCheck}
<div className="avatar small"> <div className="avatar small">
<img src={photo_url} /> <img src={photo_url} />
</div> </div>

View File

@ -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 =
`<div key="done" className="done-action">
<div>You now have {this.state.gifted_jamtracks} JamTracks credits on your account!</div>
<div><a className="go-browse" href="/client#/jamtrack">go to JamTracks home page</a></div>
</div>`
else
if this.state.purchase_required
button =
`<div key="done" className="done-action jam-class purchase-needed">
<div>You have claimed <span className="amount-gifted">{this.state.jamclass_credits}</span> 30-minute JamClass credits, but still need to enter your credit card info!</div>
<div><a className="go-browse" href="/client#/jamclass/lesson-payment/test-drive">go to the lesson payment page</a></div>
</div>`
else
button =
`<div key="done" className="done-action jam-class all-done">
<div>You now have <span className="amount-gifted">{this.state.jamclass_credits}</span> 30-minute JamClass credits on your account!</div>
<div><a className="go-browse" href="/client#/jamclass/searchOptions">go to JamClass teacher search page</a></div>
</div>`
else
if context.JK.currentUserId?
if @state.processing
buttonText = 'ACTIVATING COUPON CODE...'
else
buttonText = 'ACTIVATE COUPON CODE'
else
if @state.processing
buttonText = 'ACTIVATING ACCOUNT...'
else
buttonText = 'ACTIVATE ACCOUNT'
button = `<button key="button" className={buttonClassnames} onClick={this.action}>{buttonText}</button>`
action = `<ReactCSSTransitionGroup transitionName="session-track-list">
{button}
</ReactCSSTransitionGroup>`
title = 'Activate Account'
if context.JK.currentUserId?
form =
`<form onSubmit={this.submit}>
<label>Coupon Code:</label><input type="text" name="code"/>
{action}
</form>`
instruments = `<p className="instructions">Enter the 10-digit code you received in your email and click the Activate button below.</p>`
else
form =
`<form onSubmit={this.submit}>
<label>Coupon Code:</label><input type="text" name="code"/>
<label>Email:</label><input type="text" name="email"/>
<label>Password:</label><input type="password" name="password"/>
<div className="clearall"/>
<input className="terms-checkbox" type="checkbox" name="terms" /><label className="terms-help">I have read and agree to the JamKazam <a href="/corp/terms" onClick={this.termsClicked}>terms of service</a></label>
<div className="clearall"/>
{action}
</form>`
instruments = `<p className="instructions">Enter the 10-digit code you received in your email and click the Activate button below.</p>`
classes = classNames({'redeem-container': true, 'not-logged-in': !context.JK.currentUserId?, 'logged-in': context.JK.currentUserId? })
`<div className={classes}>
<div className="redeem-content">
<h2>{title}</h2>
{instruments}
{form}
<div className={classNames({'errors': true, 'active': this.state.formErrors})}>
{errorText}
</div>
</div>
</div>`
getInitialState: () ->
{formErrors: null, processing:false, gifted_jamtracks: null, jamclass_credits: null, purchase_required: null}
privacyPolicy: (e) ->
e.preventDefault()
context.JK.popExternalLink('/corp/privacy')
termsClicked: (e) ->
e.preventDefault()
context.JK.popExternalLink('/corp/terms')
componentDidMount:() ->
$root = $(this.getDOMNode())
$checkbox = $root.find('.terms-checkbox')
console.log("$checkbox", $checkbox)
context.JK.checkbox($checkbox)
submit: (e) ->
@action(e)
action: (e) ->
if @state.done || @state.processing
e.preventDefault()
return
if context.JK.currentUserId?
@redeem(e)
else
@signup(e)
redeem: (e) ->
e.preventDefault()
return if @state.done || @state.processing
$root = $(@getDOMNode())
$code = $root.find('input[name="code"]')
code = $code.val()
@setState({processing:true})
rest.redeemGiftCard({gift_card: code})
.done((response) =>
@setState({formErrors: null, processing:false, done: true, gifted_jamtracks: response.gifted_jamtracks, jamclass_credits: response.jamclass_credits, purchase_required: response.purchase_required})
).fail((jqXHR) =>
@setState({processing:false})
if jqXHR.status == 422
response = JSON.parse(jqXHR.responseText)
if response.errors
@setState({formErrors: response.errors})
else
context.JK.app.notify({title: 'Unknown Error', text: jqXHR.responseText})
else
context.JK.app.notifyServerError(jqXHR, "Unable to Apply Coupon")
)
signup: (e) ->
e.preventDefault()
return if @state.done || @state.processing
$root = $(@getDOMNode())
$email = $root.find('input[name="email"]')
$code = $root.find('input[name="code"]')
$password = $root.find('input[name="password"]')
terms = $root.find('input[name="terms"]').is(':checked')
@setState({processing:true})
email = $email.val()
password = $password.val()
code = $code.val()
if !code
# must pass up non-null value to indicate user is trying to redeem giftcard while creating account
code = ''
rest.signup({email: email, password: password, gift_card: code, terms: terms})
.done((response) =>
@setState({formErrors: null, processing:false, done: true, gifted_jamtracks: response.gifted_jamtracks, jamclass_credits: response.jamclass_credits, purchase_required: response.purchase_required})
).fail((jqXHR) =>
@setState({processing:false})
if jqXHR.status == 422
response = JSON.parse(jqXHR.responseText)
if response.errors
@setState({formErrors: response.errors})
else
context.JK.app.notify({title: 'Unknown Signup Error', text: jqXHR.responseText})
else
context.JK.app.notifyServerError(jqXHR, "Unable to Sign Up")
)
})

View File

@ -35,11 +35,18 @@ badCode = 'This is not a valid code. Please carefully re-enter the code and try
<div><a className="go-browse" href="/client#/jamtrack">go to JamTracks home page</a></div> <div><a className="go-browse" href="/client#/jamtrack">go to JamTracks home page</a></div>
</div>` </div>`
else else
button = if this.state.purchase_required
`<div key="done" className="done-action"> button =
<div>You now have {this.state.gifted_jamclass}, 30-minute JamClass credits on your account!</div> `<div key="done" className="done-action">
<div><a className="go-browse" href="/client#/jamclass/searchOptions">go to JamClass teacher search page</a></div> <div>You have claimed {this.state.gifted_jamclass} 30-minute JamClass credits, but still need to enter your credit card info!</div>
</div>` <div><a className="go-browse" href="/client#/jamclass/lesson-payment/test-drive">go to the lesson payment page</a></div>
</div>`
else
button =
`<div key="done" className="done-action">
<div>You now have {this.state.gifted_jamclass}, 30-minute JamClass credits on your account!</div>
<div><a className="go-browse" href="/client#/jamclass/searchOptions">go to JamClass teacher search page</a></div>
</div>`
else else
button = `<button key="button" className={buttonClassnames} onClick={this.action}>REDEEM GIFT CARD</button>` button = `<button key="button" className={buttonClassnames} onClick={this.action}>REDEEM GIFT CARD</button>`
@ -66,7 +73,7 @@ badCode = 'This is not a valid code. Please carefully re-enter the code and try
<div className="clearall"/> <div className="clearall"/>
{action} {action}
</form>` </form>`
instruments = `<p className="instructions">Enter the 10-digit code from the back of your gift card and click the Redeem button below.</p>` instruments = `<p className="instructions">Enter the 10-digit code from the back of your gift card and click the Redeem button below.</p>`
classes = classNames({'redeem-container': true, 'not-logged-in': !context.JK.currentUserId?, 'logged-in': context.JK.currentUserId? }) classes = classNames({'redeem-container': true, 'not-logged-in': !context.JK.currentUserId?, 'logged-in': context.JK.currentUserId? })
@ -121,10 +128,13 @@ badCode = 'This is not a valid code. Please carefully re-enter the code and try
$code = $root.find('input[name="code"]') $code = $root.find('input[name="code"]')
code = $code.val() code = $code.val()
@setState({processing:true})
rest.redeemGiftCard({gift_card: code}) rest.redeemGiftCard({gift_card: code})
.done((response) => .done((response) =>
@setState({formErrors: null, processing:false, done: true, gifted_jamtracks: response.gifted_jamtracks, gifted_jamclass: response.gifted_jamclass}) @setState({formErrors: null, processing:false, done: true, gifted_jamtracks: response.gifted_jamtracks, gifted_jamclass: response.gifted_jamclass, purchase_required: response.purchase_required})
).fail((jqXHR) => ).fail((jqXHR) =>
@setState({processing:false}) @setState({processing:false})
@ -161,7 +171,7 @@ badCode = 'This is not a valid code. Please carefully re-enter the code and try
rest.signup({email: email, password: password, gift_card: code, terms: terms}) rest.signup({email: email, password: password, gift_card: code, terms: terms})
.done((response) => .done((response) =>
@setState({formErrors: null, processing:false, done: true, gifted_jamtracks: response.gifted_jamtracks, gifted_jamclass: response.gifted_jamclass}) @setState({formErrors: null, processing:false, done: true, gifted_jamtracks: response.gifted_jamtracks, gifted_jamclass: response.gifted_jamclass, purchase_required: response.purchase_required})
).fail((jqXHR) => ).fail((jqXHR) =>
@setState({processing:false}) @setState({processing:false})

View File

@ -41,9 +41,9 @@ teacherActions = window.JK.Actions.Teacher
else if lesson.status == 'canceled' else if lesson.status == 'canceled'
lesson.displayStatus = 'Canceled' lesson.displayStatus = 'Canceled'
if lesson.student_canceled if lesson.student_canceled
lesson.displayStatus = 'Canceled (Student)' lesson.displayStatus = 'Canceled (by Student)'
else if lesson.teacher_canceled else if lesson.teacher_canceled
lesson.displayStatus = 'Canceled (Teacher)' lesson.displayStatus = 'Canceled (by Teacher)'
else if lesson.status == 'suspended' else if lesson.status == 'suspended'
lesson.displayStatus = 'Suspended' lesson.displayStatus = 'Suspended'
@ -60,15 +60,15 @@ teacherActions = window.JK.Actions.Teacher
if lesson.analysis?.teacher_analysis?.missed && lesson.analysis?.student_analysis?.missed if lesson.analysis?.teacher_analysis?.missed && lesson.analysis?.student_analysis?.missed
lesson.missedRole = 'both student and teacher' lesson.missedRole = 'both student and teacher'
lesson.missedUser = lesson.teacher lesson.missedUser = lesson.teacher
lesson.displayStatus = 'Missed (Both)' lesson.displayStatus = 'Missed (by Both)'
else if lesson.analysis?.teacher_analysis?.missed else if lesson.analysis?.teacher_analysis?.missed
lesson.missedRole = 'the teacher' lesson.missedRole = 'the teacher'
lesson.missedUser = lesson.teacher lesson.missedUser = lesson.teacher
lesson.displayStatus = 'Missed (Teacher)' lesson.displayStatus = 'Missed (by Teacher)'
else if lesson.analysis?.student_analysis?.missed else if lesson.analysis?.student_analysis?.missed
lesson.missedRole = 'the student' lesson.missedRole = 'the student'
lesson.missedUser = lesson.student lesson.missedUser = lesson.student
lesson.displayStatus = 'Missed (Student)' lesson.displayStatus = 'Missed (by Student)'
else else
lesson.displayStatus = 'Missed' lesson.displayStatus = 'Missed'

View File

@ -62,6 +62,14 @@
.backgroundCheck { .backgroundCheck {
margin-top:20px; margin-top:20px;
img {
margin-left: 10px;
position: relative;
top: 4px;
}
.background-check-time {
margin-top:10px;
}
} }
.introductory-video { .introductory-video {

View File

@ -98,6 +98,11 @@
} }
} }
.background-check {
position: absolute;
top: 34px;
left: 24px;
}
.user-avatar { .user-avatar {
text-align:center; text-align:center;
float:left; float:left;

View File

@ -0,0 +1,90 @@
@import "client/common";
body.web.account_activate {
h2 {
margin-bottom:20px;
}
label{
margin-bottom:4px;
color:$ColorTextTypical;
}
input{
margin-bottom:20px;
width:200px;
}
.redeem-container {
margin-left:350px;
width:400px;
padding-top:20px;
&.logged-in {
button {
margin-top:10px !important;
}
}
&.not-logged-in {
}
}
.redeem-content {
}
p.instructions {
line-height:125%;
color:$ColorTextTypical;
margin-bottom:20px;
}
button {
display:block !important;
height: 29px !important;
margin-bottom: 10px;
margin-right: 0px;
font-size: 16px !important;
padding: 7px 3px !important;
line-height:inherit !important;
margin-left:2px !important;
margin-top:15px;
}
.icheckbox_minimal {
float: left;
top: -2px;
margin-left: 0;
margin-right:10px;
}
.errors {
font-size:14px;
height:20px;
margin:0;
visibility: hidden;
color: red;
font-weight: bold;
&.active {
visibility: visible;
}
}
form {
margin-bottom:20px;
}
.terms-help {
float:left;
margin-top:-5px;
font-size:12px;
width:178px;
}
.done-action {
margin-top: 20px;
line-height: 125%;
}
}

View File

@ -343,6 +343,35 @@ class ApiMusicSessionsController < ApiController
end end
render :json => {}, :status => :ok render :json => {}, :status => :ok
end end
begin
lesson_link = nil
session_link = @history.music_session.admin_url
if @history.music_session.lesson_session
session_type = "Lesson"
lesson_link = @history.music_session.lesson_session.admin_url
else
session_type = "Session"
end
subject = "#{current_user.name} Rated Their #{session_type}!"
body = "Session Type: #{session_type}\n"
body << "Session Rating: #{@history.good_rating? ? "Good" : "Bad"}\n"
body << "User: #{current_user.email}\n"
body << "Music Session URL: #{session_link}\n"
if lesson_link
body << "Lesson URL: #{lesson_link}\n"
end
body << "Session Comments: #{@history.rating_comment}\n"
AdminMailer.jamclass_alerts({subject: subject, body: body}).deliver_now
rescue Exception => e
logger.error("Exception sending out ratings email. Boo #{e}")
end
elsif request.get? elsif request.get?
render :json => { :should_rate_session => @history.should_rate_session? }, :status => :ok render :json => { :should_rate_session => @history.should_rate_session? }, :status => :ok
end end

View File

@ -25,9 +25,10 @@ class ApiUsersController < ApiController
end end
def calendar def calendar
@user=lookup_user #@user=lookup_user
ics = CalendarManager.new.create_ics_feed(@user) #ics = CalendarManager.new.create_ics_feed(@user)
send_data ics, :filename => 'JamKazam', :disposition => 'inline', :type => "text/calendar" #send_data ics, :filename => 'JamKazam', :disposition => 'inline', :type => "text/calendar"
render :json => {}, :status => 200
end end
def show def show
@ -1015,12 +1016,12 @@ class ApiUsersController < ApiController
if @posa_card.errors.any? if @posa_card.errors.any?
respond_with_model(@posa_card) respond_with_model(@posa_card)
else else
if @posa_card.card_type == PosaCard::JAM_CLASS_4 if @posa_card.is_lesson_posa_card?
render json: {gifted_jamclass: 4}, status: 200 render json: {gifted_jamclass: @posa_card.credits}, status: 200
elsif @posa_card.card_type == PosaCard::JAM_TRACKS_10 elsif @posa_card.card_type == PosaCard::JAM_TRACKS_10
render json: {gifted_jamtracks: 10}, status: 200 render json: {gifted_jamtracks: @posa_card.credits}, status: 200
elsif @posa_card.card_type == PosaCard::JAM_TRACKS_5 elsif @posa_card.card_type == PosaCard::JAM_TRACKS_5
render json: {gifted_jamtracks: 5}, status: 200 render json: {gifted_jamtracks: @posa_card.credits}, status: 200
else else
raise 'unknown card_type ' + @posa_card.card_type raise 'unknown card_type ' + @posa_card.card_type
end end

View File

@ -310,6 +310,12 @@ class LandingsController < ApplicationController
render 'redeem_giftcard', layout: 'web' render 'redeem_giftcard', layout: 'web'
end end
def account_activate
@no_landing_tag = true
@landing_tag_play_learn_earn = true
render 'account_activate', layout: 'web'
end
def buy_gift_card def buy_gift_card
@no_landing_tag = true @no_landing_tag = true
@landing_tag_play_learn_earn = true @landing_tag_play_learn_earn = true

View File

@ -1,6 +1,6 @@
object @lesson_booking object @lesson_booking
attributes :id, :status, :lesson_type, :payment_style, :recurring, :teacher_id, :description, :lesson_length, :created_at, :user_id, :active, :accepter_id, :canceler_id, :cancel_message, :booked_price, :card_presumed_ok, :no_slots, :posa_card_id attributes :id, :status, :lesson_type, :payment_style, :recurring, :teacher_id, :description, :lesson_length, :created_at, :user_id, :active, :accepter_id, :canceler_id, :cancel_message, :booked_price, :card_presumed_ok, :no_slots, :posa_card_id, :posa_card_purchased, :remaining_roll_forward_amount_in_cents
child(:lesson_booking_slots => :slots) { child(:lesson_booking_slots => :slots) {
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :pretty_scheduled_start, :message, :pretty_start_time, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :pretty_scheduled_start, :message, :pretty_start_time, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone, :from_package

View File

@ -4,7 +4,7 @@ object @lesson_session
:status, :student_canceled, :teacher_canceled, :student_canceled_at, :teacher_canceled_at, :student_canceled_reason, :status, :student_canceled, :teacher_canceled, :student_canceled_at, :teacher_canceled_at, :student_canceled_reason,
:teacher_canceled_reason, :status, :success, :teacher_unread_messages, :student_unread_messages, :is_active?, :recurring, :teacher_canceled_reason, :status, :success, :teacher_unread_messages, :student_unread_messages, :is_active?, :recurring,
:analysed, :school_on_school?, :no_school_on_school_payment?, :payment_if_school_on_school?, :teacher_id, :student_id, :pretty_scheduled_start, :scheduled_start, :teacher_short_canceled, :analysed, :school_on_school?, :no_school_on_school_payment?, :payment_if_school_on_school?, :teacher_id, :student_id, :pretty_scheduled_start, :scheduled_start, :teacher_short_canceled,
:best_display_time :best_display_time, :remaining_roll_forward_amount_in_cents
node do |lesson_session| node do |lesson_session|
{ {

View File

@ -1,15 +1,18 @@
object @lesson object @lesson
if @lesson if @lesson # is a LessonBooking
node :lesson do |lesson| node :lesson do |lesson|
{id: @lesson.id } {
id: @lesson.id,
teacher_id: @lesson.teacher_id
}
end end
end end
if @test_drive if @test_drive # is a Sale object
node :test_drive do |lesson| node :test_drive do |lesson|
{teacher_id: @test_drive.id} {id: @test_drive.id}
end end
end end
@ -29,7 +32,7 @@ end
if @lesson_package_type if @lesson_package_type
node :lesson_package_type do |lesson_package_type| node :lesson_package_type do |lesson_package_type|
{package_type: @lesson_package_type.package_type} {package_type: @lesson_package_type.package_type, credits: @lesson_package_type.test_drive_count}
end end
end end

View File

@ -36,7 +36,8 @@ attributes :id,
:test_drives_per_week, :test_drives_per_week,
:errors, :errors,
:profile_pct, :profile_pct,
:school_id :school_id,
:background_check_at
child :review_summary => :review_summary do child :review_summary => :review_summary do

View File

@ -34,7 +34,7 @@ end
# give back more info if the user being fetched is yourself # give back more info if the user being fetched is yourself
if current_user && @user == current_user if current_user && @user == current_user
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :show_whats_next_count, :subscribe_email, :auth_twitter, :new_notifications, :sales_count, :reuse_card, :purchased_jamtracks_count, :first_downloaded_client_at, :created_at, :first_opened_jamtrack_web_player, :gifted_jamtracks, :has_redeemable_jamtrack, :remaining, :has_stored_credit_card?, :remaining_test_drives, :jamclass_credits, :can_buy_test_drive?, :lesson_package_type_id, :school_id, :is_guitar_center_student? attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :show_whats_next_count, :subscribe_email, :auth_twitter, :new_notifications, :sales_count, :reuse_card, :purchased_jamtracks_count, :first_downloaded_client_at, :created_at, :first_opened_jamtrack_web_player, :gifted_jamtracks, :has_redeemable_jamtrack, :remaining, :has_stored_credit_card?, :remaining_test_drives, :jamclass_credits, :can_buy_test_drive?, :lesson_package_type_id, :school_id, :is_guitar_center_student?, :purchase_required, :lesson_package_needs_purchase_id
node :owned_school_id do |user| node :owned_school_id do |user|
user.owned_school.id if user.owned_school user.owned_school.id if user.owned_school

View File

@ -4,7 +4,7 @@ object @user
attributes :id, :first_name, :last_name, :name, :photo_url attributes :id, :first_name, :last_name, :name, :photo_url
child :teacher do |teacher| child :teacher do |teacher|
attributes :id, :biography, :school_id attributes :id, :biography, :school_id, :background_check_at
child :school do |school| child :school do |school|
attributes :id, :education attributes :id, :education

View File

@ -0,0 +1,5 @@
- provide(:page_name, 'landing_page full account_activate')
- provide(:description, 'Here you can redeem a code and associate it with your JamKazam account.')
- provide(:title, 'Activate Account')
= react_component 'AccountActivatePage'

View File

@ -251,6 +251,7 @@ if defined?(Bundler)
config.email_social_alias = 'social@jamkazam.com' config.email_social_alias = 'social@jamkazam.com'
config.email_crashes_alias = 'clientcrash@jamkazam.com' config.email_crashes_alias = 'clientcrash@jamkazam.com'
config.email_alerts_alias = 'alerts@jamkazam.com' # should be used for 'oh no' server down/service down sorts of emails config.email_alerts_alias = 'alerts@jamkazam.com' # should be used for 'oh no' server down/service down sorts of emails
config.email_jamclass_alerts_alias= 'jamclass-alerts@jamkazam.com'
config.email_generic_from = 'nobody@jamkazam.com' config.email_generic_from = 'nobody@jamkazam.com'
config.email_recurly_notice = 'recurly-alerts@jamkazam.com' config.email_recurly_notice = 'recurly-alerts@jamkazam.com'
config.email_smtp_address = 'smtp.sendgrid.net' config.email_smtp_address = 'smtp.sendgrid.net'

View File

@ -90,6 +90,7 @@ SampleApp::Application.configure do
config.minimal_curtain = true config.minimal_curtain = true
config.video_available= ENV['VIDEO_AVAILABILITY'] || "full" config.video_available= ENV['VIDEO_AVAILABILITY'] || "full"
config.email_generic_from = 'nobody-dev@jamkazam.com' config.email_generic_from = 'nobody-dev@jamkazam.com'
config.email_jamclass_alerts_alias= ENV['ALERT_EMAIL'] || 'jamclass-alerts-dev@jamkazam.com'
config.email_alerts_alias = ENV['ALERT_EMAIL'] || 'alerts-dev@jamkazam.com' config.email_alerts_alias = ENV['ALERT_EMAIL'] || 'alerts-dev@jamkazam.com'
config.email_crashes_alias = ENV['ALERT_EMAIL'] || 'clientcrash-dev@jamkazam.com' config.email_crashes_alias = ENV['ALERT_EMAIL'] || 'clientcrash-dev@jamkazam.com'
config.email_social_alias = ENV['ALERT_EMAIL'] || 'social-dev@jamkazam.com' config.email_social_alias = ENV['ALERT_EMAIL'] || 'social-dev@jamkazam.com'

View File

@ -53,7 +53,7 @@ SampleApp::Application.configure do
config.websocket_gateway_enable = false config.websocket_gateway_enable = false
config.websocket_gateway_port = 6759 config.websocket_gateway_port = 6759
config.websocket_gateway_uri = "ws://localhost:#{config.websocket_gateway_port}/websocket" config.websocket_gateway_uri = "ws://127.0.0.1:#{config.websocket_gateway_port}/websocket"
#config.websocket_gateway_connect_time_stale_client = 4 #config.websocket_gateway_connect_time_stale_client = 4
#config.websocket_gateway_connect_time_expire_client = 6 #config.websocket_gateway_connect_time_expire_client = 6

View File

@ -22,6 +22,7 @@ Rails.application.routes.draw do
delete '/signout', to: 'sessions#destroy' delete '/signout', to: 'sessions#destroy'
match '/redeem_giftcard', to: 'landings#redeem_giftcard', via: :get match '/redeem_giftcard', to: 'landings#redeem_giftcard', via: :get
match '/account/activate/code', to: 'landings#account_activate', via: :get
# landing pageslanding # landing pageslanding
get '/jamtracks', to: 'landings#simple_jamtracks', as: 'landing_simple_jamtracks' get '/jamtracks', to: 'landings#simple_jamtracks', as: 'landing_simple_jamtracks'

View File

@ -13,8 +13,70 @@ rescue TypeError
puts "for production, we ignore type error" puts "for production, we ignore type error"
end end
def generate
[*('a'..'z'),*('0'..'9')].shuffle[0,10].join.upcase
end
def gc_10
CSV.open("gift-card-10.csv", "wb") do |csv|
for i in 1..150
csv << [generate()]
end
end
end
def gc_20
CSV.open("gift-card-20.csv", "wb") do |csv|
for i in 1..100
csv << [generate()]
end
end
end
# round to. we make
#One set of 200 codes that when redeemed translate into 5 (not 10) JamTracks each.
#One set of 200 codes that when redeemed translate into 4 JamClass lessons each.
def gc_5jt_2
CSV.open("posa-cards-jt-5.csv", "wb") do |csv|
for i in 1..250
csv << [generate()]
end
end
end
def gc_4jc_2
CSV.open("posa-cards-jc-4.csv", "wb") do |csv|
for i in 1..250
csv << [generate()]
end
end
end
namespace :lessons do namespace :lessons do
task amazon_gift_cards: :environment do|task, args|
CSV.open("posa-cards-amazon-test-drive-paid-4.csv", "wb") do |csv|
for i in 1..250
csv << [generate(), 'amazon-test-drive-paid-4', true, true]
end
end
CSV.open("posa-cards-amazon-test-drive-free-4.csv", "wb") do |csv|
for i in 1..250
csv << [generate(), 'amazon-test-drive-free-4',true, false]
end
end
CSV.open("posa-cards-amazon-test-drive-free-2.csv", "wb") do |csv|
for i in 1..250
csv << [generate(), 'amazon-test-drive-free-2',true, false]
end
end
end
task book_completed: :environment do |task, args| task book_completed: :environment do |task, args|
user = User.find_by_email(ENV['STUDENT']) user = User.find_by_email(ENV['STUDENT'])
@ -68,7 +130,7 @@ namespace :lessons do
end end
booking = LessonBooking.book_normal(user, teacher, slots, "Hey I've heard of you before.", recurring, payment_style, 60) booking = LessonBooking.book_normal(user, teacher, slots, "Hey I've heard of you before.", recurring, payment_style, 30)
if booking.errors.any? if booking.errors.any?
puts booking.errors.inspect puts booking.errors.inspect
raise "booking failed" raise "booking failed"

66
web/posa_cards.rb Normal file
View File

@ -0,0 +1,66 @@
def generate
[*('a'..'z'),*('0'..'9')].shuffle[0,10].join.upcase
end
def gc_10
CSV.open("gift-card-10.csv", "wb") do |csv|
for i in 1..150
csv << [generate()]
end
end
end
def gc_20
CSV.open("gift-card-20.csv", "wb") do |csv|
for i in 1..100
csv << [generate()]
end
end
end
# round to. we make
#One set of 200 codes that when redeemed translate into 5 (not 10) JamTracks each.
#One set of 200 codes that when redeemed translate into 4 JamClass lessons each.
def gc_5jt_2
CSV.open("posa-cards-jt-5.csv", "wb") do |csv|
for i in 1..250
csv << [generate()]
end
end
end
def gc_4jc_2
CSV.open("posa-cards-jc-4.csv", "wb") do |csv|
for i in 1..250
csv << [generate()]
end
end
end
def amazon_gift_cards
CSV.open("posa-cards-amazon-test-drive-paid-4.csv", "wb") do |csv|
for i in 1..250
csv << [generate(), 'amazon-test-drive-paid-4', true, true]
end
end
CSV.open("posa-cards-amazon-test-drive-free-4.csv", "wb") do |csv|
for i in 1..250
csv << [generate(), 'amazon-test-drive-free-4',true, false]
end
end
CSV.open("posa-cards-amazon-test-drive-free-2.csv", "wb") do |csv|
for i in 1..250
csv << [generate(), 'amazon-test-drive-free-2',true, false]
end
end
end

View File

@ -34,11 +34,12 @@ describe ApiJamblastersController, type: :controller do
response.status.should == 200 response.status.should == 200
end end
it "disallows different user" do
user2 = FactoryGirl.create(:user) #it "disallows different user" do
get :is_allowed, {:format => 'json', jbid: jamblaster.client_id, user_id: user2.id} # user2 = FactoryGirl.create(:user)
response.status.should == 403 # get :is_allowed, {:format => 'json', jbid: jamblaster.client_id, user_id: user2.id}
end # response.status.should == 403
#end
end end
describe "not already associated" do describe "not already associated" do

View File

@ -870,7 +870,41 @@ FactoryGirl.define do
factory :posa_card, class: 'JamRuby::PosaCard' do factory :posa_card, class: 'JamRuby::PosaCard' do
sequence(:code) { |n| n.to_s } sequence(:code) { |n| n.to_s }
card_type JamRuby::PosaCardType::JAM_TRACKS_5 card_type JamRuby::PosaCard::JAM_TRACKS_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
factory :amazon_2_free do
card_type JamRuby::PosaCard::JAM_CLASS_2
credits 2
lesson_package_type { JamRuby::LessonPackageType.amazon_test_drive_free_2 }
is_lesson true
preactivate true
end
factory :amazon_4_paid do
card_type JamRuby::PosaCard::JAM_CLASS_4
credits 2
lesson_package_type { JamRuby::LessonPackageType.amazon_test_drive_paid_4 }
is_lesson true
requires_purchase true
purchased false
preactivate true
end
end end
factory :posa_card_type, class: 'JamRuby::PosaCardType' do factory :posa_card_type, class: 'JamRuby::PosaCardType' do

View File

@ -0,0 +1,126 @@
require 'spec_helper'
# tests what happens when the websocket connection goes away
describe "Activate Account Card", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
let(:user1) { FactoryGirl.create(:user) }
let(:amazon_2_free_card) { FactoryGirl.create(:amazon_2_free) }
before(:all) do
User.delete_all
end
describe "not logged in" do
describe "amazon_2_free_card" do
it "succeeds" do
visit '/account/activate/code'
amazon_2_free_card.credits.should eql 2
find('h2', text: 'Activate Account')
fill_in "code", with: amazon_2_free_card.code
fill_in "email", with: "amzposa1@jamkazam.com"
fill_in "password", with: "jam123"
find('.redeem-container ins', visible: false).trigger(:click)
find('button.redeem-giftcard', text: 'ACTIVATE ACCOUNT').trigger(:click)
find('.jam-class.all-done span.amount-gifted', text: amazon_2_free_card.credits)
find('.done-action a.go-browse').trigger(:click)
find('h2', text: 'search teachers')
user = User.find_by_email("amzposa1@jamkazam.com")
amazon_2_free_card.reload
amazon_2_free_card.user.should eq(user)
amazon_2_free_card.requires_purchase.should be false
amazon_2_free_card.purchased.should be true
user.reload
user.jamclass_credits.should eq(amazon_2_free_card.credits)
end
it "validates correctly" do
visit '/account/activate/code'
find('h2', text: 'Activate Account')
find('button.redeem-giftcard', text: 'ACTIVATE ACCOUNT').trigger(:click)
find('.errors.active', text: "Email can't be blank")
find('h2', text: 'Activate Account')
fill_in "code", with: amazon_2_free_card.code
fill_in "email", with: "amzpos2@jamkazam.com"
fill_in "password", with: "jam123"
find('.redeem-container ins', visible: false).trigger(:click)
find('button.redeem-giftcard', text: 'ACTIVATE ACCOUNT').trigger(:click)
find('.done-action a.go-browse').trigger(:click)
find('h2', text: 'search teachers')
user = User.find_by_email("amzpos2@jamkazam.com")
amazon_2_free_card.reload
amazon_2_free_card.user.should eq(user)
amazon_2_free_card.requires_purchase.should be false
amazon_2_free_card.purchased.should be true
user.reload
user.jamclass_credits.should eq(amazon_2_free_card.credits)
end
end
end
describe "logged in" do
it "succeeds" do
fast_signin(user1, '/account/activate/code')
find('h2', text: 'Activate Account')
fill_in "code", with: amazon_2_free_card.code
find('button.redeem-giftcard', text: 'ACTIVATE COUPON CODE').trigger(:click)
find('.done-action a.go-browse').trigger(:click)
find('h2', text: 'search teachers')
user1.reload
amazon_2_free_card.reload
amazon_2_free_card.user.should eq(user)
amazon_2_free_card.requires_purchase.should be false
amazon_2_free_card.purchased.should be true
user.jamclass_credits.should eq(amazon_2_free_card.credits)
end
end
describe "logged in" do
it "validates" do
fast_signin(user1, '/account/activate/code')
find('h2', text: 'Activate Account')
find('button.redeem-giftcard').trigger(:click)
find('.errors.active', text: "Coupon Code does not exist")
fill_in "code", with: amazon_2_free_card.code
find('button.redeem-giftcard').trigger(:click)
find('.done-action a.go-browse').trigger(:click)
find('h2', text: 'search teachers')
user1.reload
amazon_2_free_card.reload
amazon_2_free_card.user.should eq(user)
amazon_2_free_card.requires_purchase.should be false
amazon_2_free_card.purchased.should be true
user.jamclass_credits.should eq(amazon_2_free_card.credits)
end
end
end

View File

@ -7,7 +7,7 @@ describe "Test Drive", :js => true, :type => :feature, :capybara_feature => true
let(:user) { FactoryGirl.create(:user, traditional_band: true,paid_sessions: true, paid_sessions_hourly_rate: 1, paid_sessions_daily_rate:1 ) } let(:user) { FactoryGirl.create(:user, traditional_band: true,paid_sessions: true, paid_sessions_hourly_rate: 1, paid_sessions_daily_rate:1 ) }
let(:teacher_user) {FactoryGirl.create(:teacher_user, ready_for_session_at: Time.now)} let(:teacher_user) {FactoryGirl.create(:teacher_user, ready_for_session_at: Time.now)}
let(:teacher_user2) {FactoryGirl.create(:teacher_user, ready_for_session_at: Time.now)} let(:teacher_user2) {FactoryGirl.create(:teacher_user, ready_for_session_at: Time.now)}
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)} let(:retailer) {FactoryGirl.create(:retailer)}
before(:each) do before(:each) do

View File

@ -12,6 +12,13 @@ describe UsersController, :type => :api do
#login(authenticated_user.email, authenticated_user.password, 200, true) #login(authenticated_user.email, authenticated_user.password, 200, true)
} }
def login(user)
# login as fan
post '/api/auth_session.json', { :email => user.email, :password => user.password }.to_json, "CONTENT_TYPE" => 'application/json'
last_response.status.should == 200
JSON.parse(last_response.body).should == { "success" => true }
end
it "unsubscribe" do it "unsubscribe" do
user.subscribe_email.should eql true user.subscribe_email.should eql true
get '/unsubscribe/' + user.unsubscribe_token get '/unsubscribe/' + user.unsubscribe_token
@ -73,13 +80,13 @@ describe UsersController, :type => :api do
describe "logged in" do describe "logged in" do
it "should not set origin with no referrer info" do it "should not set origin with no referrer info" do
controller.current_user = user login(user)
get :home get :home
response.cookies["origin"].should be_nil response.cookies["origin"].should be_nil
end end
it "should not set origin with referrer info" do it "should not set origin with referrer info" do
controller.current_user = user login(user)
get :home, utm_valid_cookie get :home, utm_valid_cookie
response.cookies["origin"].should be_nil response.cookies["origin"].should be_nil
end end

View File

@ -92,7 +92,7 @@ Thread.new do
:connect_time_stale_browser => 4, :connect_time_stale_browser => 4,
:connect_time_expire_browser => 6, :connect_time_expire_browser => 6,
:max_connections_per_user => 20, :max_connections_per_user => 20,
:rabbitmq_host => 'localhost', :rabbitmq_host => '127.0.0.1',
:rabbitmq_port => 5672, :rabbitmq_port => 5672,
:calling_thread => current, :calling_thread => current,
:cidr => ['0.0.0.0/0'], :cidr => ['0.0.0.0/0'],

View File

@ -25,13 +25,13 @@ development:
test: test:
port: 6759 port: 6759
verbose: true verbose: true
rabbitmq_host: localhost rabbitmq_host: 127.0.0.1
rabbitmq_port: 5672 rabbitmq_port: 5672
<<: *defaults <<: *defaults
production: production:
port: 6767 port: 6767
verbose: false verbose: false
rabbitmq_host: localhost rabbitmq_host: 127.0.0.1
rabbitmq_port: 5672 rabbitmq_port: 5672
<<: *defaults <<: *defaults