Merge branch 'develop' of bitbucket.org:jamkazam/jam-cloud into develop

This commit is contained in:
Jonathan Kolyer 2016-05-15 04:35:53 +00:00
commit 481bc36133
256 changed files with 8683 additions and 1335 deletions

View File

@ -73,7 +73,7 @@ gem 'iso-639'
gem 'rubyzip' gem 'rubyzip'
gem 'sanitize' gem 'sanitize'
gem 'slim' gem 'slim'
gem 'influxdb' #gem 'influxdb'
gem 'cause' # needed by influxdb gem 'cause' # needed by influxdb
gem 'influxdb-rails', '0.1.10' gem 'influxdb-rails', '0.1.10'
gem 'recurly' gem 'recurly'
@ -107,7 +107,6 @@ end
group :development, :test do group :development, :test do
gem 'capybara' gem 'capybara'
gem 'rspec-rails', '2.14.2' gem 'rspec-rails', '2.14.2'
gem 'guard-rspec'
gem 'jasmine', '1.3.1' gem 'jasmine', '1.3.1'
gem 'execjs', '1.4.0' gem 'execjs', '1.4.0'
#gem 'therubyracer' #, '0.11.0beta8' #gem 'therubyracer' #, '0.11.0beta8'

View File

@ -0,0 +1,20 @@
ActiveAdmin.register JamRuby::User, :as => 'SchoolInterest' do
menu :label => 'Interested in Schools', :parent => 'JamClass'
config.sort_order = 'created_at desc'
config.batch_actions = false
config.per_page = 100
config.paginate = true
config.filters = false
scope("All", default: true) { |scope| scope.where(school_interest: true) }
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
end
end

View File

@ -6,12 +6,13 @@ ActiveAdmin.register JamRuby::User, :as => 'Students' do
config.batch_actions = false config.batch_actions = false
config.per_page = 100 config.per_page = 100
config.paginate = true config.paginate = true
config.filters = false
def booked_anything(scope) def booked_anything(scope)
scope.joins(:student_lesson_bookings).where('lesson_bookings.active = true').uniq scope.joins(:student_lesson_bookings).where('lesson_bookings.active = true').uniq
end end
scope("Default", default: true) { |scope| booked_anything(scope).order('ready_for_session_at IS NULL DESC') } scope("Default", default: true) { |scope| scope.where('is_a_student = true OR ((select count(id) from lesson_bookings where lesson_bookings.user_id = users.id) > 0)').order('users.ready_for_session_at IS NULL DESC') }
index do index do
column "Name" do |user| column "Name" do |user|
@ -45,8 +46,8 @@ ActiveAdmin.register JamRuby::User, :as => 'Students' do
end end
end end
column "School" do |user| column "School" do |user|
if teacher.school if user.school
teacher.school.name user.school.name
end end
end end
end end

View File

@ -1,6 +1,6 @@
ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do
menu :label => 'Teacher', :parent => 'JamClass' menu :label => 'Teachers', :parent => 'JamClass'
config.sort_order = 'created_at desc' config.sort_order = 'created_at desc'
config.batch_actions = false config.batch_actions = false

View File

@ -1,18 +1,21 @@
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
little-plugger (1.1.3) little-plugger (1.1.4)
logging (1.7.2) logging (1.7.2)
little-plugger (>= 1.1.3) little-plugger (>= 1.1.3)
pg (0.17.1) pg (0.17.1)
pg_migrate (0.1.13) pg_migrate (0.1.14)
logging (= 1.7.2) logging (= 1.7.2)
pg (= 0.17.1) pg (= 0.17.1)
thor thor
thor (0.18.1) thor (0.19.1)
PLATFORMS PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
pg_migrate (= 0.1.13)! pg_migrate (= 0.1.14)!
BUNDLED WITH
1.11.2

View File

@ -341,4 +341,10 @@ email_blacklist.sql
jamblaster_connection.sql jamblaster_connection.sql
teacher_progression.sql teacher_progression.sql
teacher_complete.sql teacher_complete.sql
lessons.sql lessons.sql
lessons_unread_messages.sql
track_school_signups.sql
add_test_drive_types.sql
updated_subjects.sql
update_payment_history.sql
lesson_booking_schools.sql

View File

@ -0,0 +1,4 @@
INSERT INTO lesson_package_types (id, name, description, package_type, price) VALUES ('test-drive-2', 'Test Drive (2)', 'Two reduced-price lessons which you can use to find that ideal teacher.', 'test-drive-2', 29.99);
INSERT INTO lesson_package_types (id, name, description, package_type, price) VALUES ('test-drive-1', 'Test Drive (1)', 'One reduced-price lessons which you can use to find that ideal teacher.', 'test-drive-1', 15.99);
UPDATE lesson_package_types set name = 'Test Drive (4)', package_type = 'test-drive-4' WHERE id = 'test-drive';
ALTER TABLE users ADD COLUMN lesson_package_type_id VARCHAR(64) REFERENCES lesson_package_types(id);

View File

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

View File

@ -0,0 +1,34 @@
ALTER TABLE chat_messages DROP COLUMN lesson_booking_id;
ALTER TABLE chat_messages ADD COLUMN lesson_session_id VARCHAR(64) REFERENCES lesson_sessions(id);
ALTER TABLE lesson_sessions ADD COLUMN teacher_unread_messages BOOLEAN DEFAULT FALSE NOT NULL;
ALTER TABLE lesson_sessions ADD COLUMN student_unread_messages BOOLEAN DEFAULT FALSE NOT NULL;
ALTER TABLE chat_messages ADD COLUMN purpose VARCHAR(200);
ALTER TABLE lesson_sessions ADD COLUMN student_short_canceled BOOLEAN DEFAULT FALSE NOT NULL;
ALTER TABLE lesson_sessions ADD COLUMN teacher_short_canceled BOOLEAN DEFAULT FALSE NOT NULL;
ALTER TABLE lesson_sessions ADD COLUMN sent_starting_notice BOOLEAN DEFAULT FALSE NOT NULL;
ALTER TABLE lesson_bookings DROP CONSTRAINT lesson_bookings_counter_slot_id_fkey;
ALTER TABLE lesson_bookings ADD CONSTRAINT lesson_bookings_counter_slot_id_fkey FOREIGN KEY (counter_slot_id) REFERENCES lesson_booking_slots(id) ON DELETE CASCADE;
ALTER TABLE lesson_bookings DROP CONSTRAINT lesson_bookings_default_slot_id_fkey;
ALTER TABLE lesson_bookings ADD CONSTRAINT lesson_bookings_default_slot_id_fkey FOREIGN KEY (default_slot_id) REFERENCES lesson_booking_slots(id) ON DELETE CASCADE;
ALTER TABLE lesson_sessions DROP CONSTRAINT lesson_sessions_slot_id_fkey;
ALTER TABLE lesson_sessions ADD CONSTRAINT lesson_sessions_slot_id_fkey FOREIGN KEY (slot_id) REFERENCES lesson_booking_slots(id) ON DELETE CASCADE;
ALTER TABLE users DROP CONSTRAINT users_teacher_id_fkey;
ALTER TABLE users ADD CONSTRAINT users_teacher_id_fkey FOREIGN KEY (teacher_id) REFERENCES teachers(id) ON DELETE CASCADE;
ALTER TABLE music_sessions DROP CONSTRAINT music_sessions_lesson_session_id_fkey;
ALTER TABLE music_sessions ADD CONSTRAINT music_sessions_lesson_session_id_fkey FOREIGN KEY (lesson_session_id) REFERENCES lesson_sessions(id) ON DELETE SET NULL;
ALTER TABLE notifications DROP CONSTRAINT notifications_lesson_session_id_fkey;
ALTER TABLE notifications ADD CONSTRAINT notifications_lesson_session_id_fkey FOREIGN KEY (lesson_session_id) REFERENCES lesson_sessions(id) ON DELETE CASCADE;
ALTER TABLE chat_messages DROP CONSTRAINT chat_messages_lesson_session_id_fkey;
ALTER TABLE chat_messages ADD CONSTRAINT chat_messages_lesson_session_id_fkey FOREIGN KEY (lesson_session_id) REFERENCES lesson_sessions(id) ON DELETE CASCADE;
ALTER TABLE chat_messages DROP CONSTRAINT chat_messages_target_user_id_fkey;
ALTER TABLE chat_messages ADD CONSTRAINT chat_messages_target_user_id_fkey FOREIGN KEY (lesson_session_id) REFERENCES lesson_sessions(id) ON DELETE SET NULL;

View File

@ -0,0 +1 @@
ALTER TABLE USERS ADD COLUMN school_interest BOOLEAN DEFAULT FALSE;

View File

@ -0,0 +1 @@
ALTER TABLE charges ADD COLUMN user_id VARCHAR(64) REFERENCES users(id);

View File

@ -0,0 +1,27 @@
-- https://jamkazam.atlassian.net/browse/VRFS-3407
UPDATE subjects SET description = 'Composition' WHERE id = 'composing';
UPDATE subjects SET description = 'Recording & Production' WHERE id = 'recording';
UPDATE subjects SET description = 'Sight Reading' WHERE id = 'site-reading';
INSERT INTO subjects(id, description) VALUES ('film-scoring', 'Film Scoring');
INSERT INTO subjects(id, description) VALUES ('video-game-scoring', 'Video Game Scoring');
INSERT INTO subjects(id, description) VALUES ('ear-training', 'Ear Training');
INSERT INTO subjects(id, description) VALUES ('harmony', 'Harmony');
INSERT INTO subjects(id, description) VALUES ('music-therapy', 'Music Therapy');
INSERT INTO subjects(id, description) VALUES ('songwriting', 'Songwriting');
INSERT INTO subjects(id, description) VALUES ('conducting', 'Conducting');
INSERT INTO subjects(id, description) VALUES ('instrument-repair', 'Instrument Repair');
INSERT INTO subjects(id, description) VALUES ('improvisation', 'Improvisation');
INSERT INTO subjects(id, description) VALUES ('pro-tools', 'Pro Tools');
INSERT INTO subjects(id, description) VALUES ('ableton-live', 'Ableton Live');
INSERT INTO subjects(id, description) VALUES ('fl-studio', 'FL Studio');
INSERT INTO subjects(id, description) VALUES ('garageband', 'GarageBand');
INSERT INTO subjects(id, description) VALUES ('apple-logic-pro', 'Apple Logic Pro');
INSERT INTO subjects(id, description) VALUES ('presonus-studio-one', 'PreSonus Studio One');
INSERT INTO subjects(id, description) VALUES ('reaper', 'Reaper');
INSERT INTO subjects(id, description) VALUES ('cubase', 'Cubase');
INSERT INTO subjects(id, description) VALUES ('sonar', 'Sonar');
INSERT INTO subjects(id, description) VALUES ('reason', 'Reason');
INSERT INTO subjects(id, description) VALUES ('amplitube', 'AmpliTube');
INSERT INTO subjects(id, description) VALUES ('line-6-pod', 'Line 6 Pod');
INSERT INTO subjects(id, description) VALUES ('guitar-ring', 'Guitar Rig');

View File

@ -619,6 +619,8 @@ message ChatMessage {
optional string msg_id = 4; optional string msg_id = 4;
optional string created_at = 5; optional string created_at = 5;
optional string channel = 6; optional string channel = 6;
optional string lesson_session_id = 7;
optional string purpose = 8;
} }
message SendChatMessage { message SendChatMessage {

View File

@ -49,7 +49,7 @@ gem 'rest-client'
gem 'iso-639' gem 'iso-639'
gem 'rubyzip' gem 'rubyzip'
gem 'sanitize' gem 'sanitize'
gem 'influxdb' #gem 'influxdb'
gem 'recurly' gem 'recurly'
gem 'sendgrid_toolkit', '>= 1.1.1' gem 'sendgrid_toolkit', '>= 1.1.1'
gem 'stripe' gem 'stripe'

View File

@ -57,6 +57,7 @@ require "jam_ruby/resque/scheduled/unused_music_notation_cleaner"
require "jam_ruby/resque/scheduled/user_progress_emailer" require "jam_ruby/resque/scheduled/user_progress_emailer"
require "jam_ruby/resque/scheduled/daily_job" require "jam_ruby/resque/scheduled/daily_job"
require "jam_ruby/resque/scheduled/hourly_job" require "jam_ruby/resque/scheduled/hourly_job"
require "jam_ruby/resque/scheduled/minutely_job"
require "jam_ruby/resque/scheduled/daily_session_emailer" require "jam_ruby/resque/scheduled/daily_session_emailer"
require "jam_ruby/resque/scheduled/new_musician_emailer" require "jam_ruby/resque/scheduled/new_musician_emailer"
require "jam_ruby/resque/scheduled/music_session_reminder" require "jam_ruby/resque/scheduled/music_session_reminder"

View File

@ -36,6 +36,14 @@ module JamRuby
subject: options[:subject]) subject: options[:subject])
end end
def partner(options)
mail(to: APP_CONFIG.email_partners_alias,
from: APP_CONFIG.email_generic_from,
body: options[:body],
content_type: "text/plain",
subject: options[:subject])
end
def recurly_alerts(user, options) def recurly_alerts(user, options)
body = options[:body] body = options[:body]

View File

@ -81,6 +81,22 @@ module JamRuby
end end
end end
def school_owner_welcome_message(user)
@user = user
@subject= "Welcome to JamKazam and JamClass online lessons!"
sendgrid_category "Welcome"
sendgrid_unique_args :type => "welcome_message"
sendgrid_recipients([user.email])
sendgrid_substitute('@USERID', [user.id])
sendgrid_substitute(EmailBatchProgression::VAR_FIRST_NAME, [user.first_name])
mail(:to => user.email, :subject => @subject) do |format|
format.text
format.html
end
end
def password_changed(user) def password_changed(user)
@user = user @user = user
@ -912,7 +928,7 @@ module JamRuby
@lesson_session = lesson_session @lesson_session = lesson_session
email = @student.email email = @student.email
subject = "You have used #{@student.remaining_test_drives} of 4 TestDrive lesson credits" subject = "You have used #{@student.remaining_test_drives} of #{@student.total_test_drives} TestDrive lesson credits"
unique_args = {:type => "student_test_drive_success"} unique_args = {:type => "student_test_drive_success"}
sendgrid_category "Notification" sendgrid_category "Notification"
@ -927,6 +943,31 @@ module JamRuby
end end
end end
def student_test_drive_no_bill(lesson_session)
@student = lesson_session.student
@teacher = lesson_session.teacher
@session_name = lesson_session.music_session.name
@session_description = lesson_session.music_session.description
@session_date = lesson_session.slot.pretty_scheduled_start(true)
@session_url = lesson_session.web_url
@lesson_session = lesson_session
email = @student.email
subject = "Your TestDrive with #{@teacher.name} will not be billed"
unique_args = {:type => "student_test_drive_no_bill"}
sendgrid_category "Notification"
sendgrid_unique_args :type => unique_args[:type]
sendgrid_recipients([email])
sendgrid_substitute('@USERID', [@student.id])
mail(:to => email, :subject => subject) do |format|
format.text
format.html { render :layout => "from_user_mailer" }
end
end
# successfully completed, but no more test drives left # successfully completed, but no more test drives left
def student_test_drive_lesson_done(lesson_session) def student_test_drive_lesson_done(lesson_session)
@ -939,7 +980,7 @@ module JamRuby
@lesson_session = lesson_session @lesson_session = lesson_session
email = @student.email email = @student.email
subject = "You have used all 4 TestDrive lesson credits" subject = "You have used all TestDrive lesson credits"
unique_args = {:type => "student_test_drive_success"} unique_args = {:type => "student_test_drive_success"}
sendgrid_category "Notification" sendgrid_category "Notification"
@ -1229,12 +1270,39 @@ module JamRuby
end end
end end
# always goes to the teacher
def teacher_distribution_done(teacher_payment) def teacher_distribution_done(teacher_payment)
@school = teacher_payment.school
@teacher_payment = teacher_payment @teacher_payment = teacher_payment
@distribution = teacher_payment.teacher_distribution
@teacher = teacher_payment.teacher @teacher = teacher_payment.teacher
@payable_teacher = teacher_payment.payable_teacher
@name = @teacher.first_name || 'Anonymous'
@student = @distribution.student
email = @teacher.email email = @teacher.email
@subject = "You have received payment for your participation in JamClass" if @school
if @distribution.is_test_drive?
@subject = "Your TestDrive lesson with #{@student.name}"
elsif @distribution.is_normal?
@subject = "Your lesson with #{@student.name}"
elsif @distribution.is_monthly?
@subject = "Your #{@distribution.month_name} lessons with #{@student.name}"
else
@subject = "Your lesson with #{@student.name}"
end
else
if @distribution.is_test_drive?
@subject = "You have earned #{@distribution.real_distribution_display} for your TestDrive lesson with #{@student.first_name}"
elsif @distribution.is_normal?
@subject = "You have earned #{@distribution.real_distribution_display} for your lesson with #{@student.first_name}"
elsif @distribution.is_monthly?
@subject = "You have earned #{@distribution.real_distribution_display} for your #{@distribution.month_name} lessons with #{@student.first_name}"
else
@subject = "You have earned #{@distribution.real_distribution_display} for your lesson with #{@student.first_name}"
end
end
unique_args = {:type => "teacher_distribution_done"} unique_args = {:type => "teacher_distribution_done"}
sendgrid_category "Notification" sendgrid_category "Notification"
@ -1249,24 +1317,51 @@ module JamRuby
end end
end end
# if school, goes to school owner; otherwise goes to teacher
def teacher_distribution_fail(teacher_payment) def teacher_distribution_fail(teacher_payment)
@school = teacher_payment.school
@teacher_payment = teacher_payment @teacher_payment = teacher_payment
@distribution = teacher_payment.teacher_distribution
@teacher = teacher_payment.teacher @teacher = teacher_payment.teacher
email = @teacher.email @payable_teacher = teacher_payment.payable_teacher
@student = @distribution.student
@name = @payable_teacher.first_name || 'Anonymous'
email = @payable_teacher.email
@card_declined = teacher_payment.is_card_declined? @card_declined = teacher_payment.is_card_declined?
@card_expired = teacher_payment.is_card_expired? @card_expired = teacher_payment.is_card_expired?
@bill_date = teacher_payment.last_billed_at_date @bill_date = teacher_payment.last_billed_at_date
@subject = "We were unable to pay you today" if @school
if @distribution.is_test_drive?
@subject = "We had a problem paying #{@distribution.real_distribution_display} for #{@teacher.name}'s TestDrive lesson with #{@student.name}"
elsif @distribution.is_normal?
@subject = "We had a problem paying #{@distribution.real_distribution_display} for #{@teacher.name}'s lesson with #{@student.name}"
elsif @distribution.is_monthly?
@subject = "We had a problem paying #{@distribution.real_distribution_display} for #{@teacher.name}'s #{@distribution.month_name} lessons with #{@student.name}"
else
@subject = "We had a problem paying #{@distribution.real_distribution_display} for #{@teacher.name}'s lesson with #{@student.name}"
end
else
if @distribution.is_test_drive?
@subject = "We had a problem paying you #{@distribution.real_distribution_display} for your TestDrive lesson with #{@student.first_name}"
elsif @distribution.is_normal?
@subject = "We had a problem paying you #{@distribution.real_distribution_display} for your lesson with #{@student.first_name}"
elsif @distribution.is_monthly?
@subject = "We had a problem paying you #{@distribution.real_distribution_display} for your #{@distribution.month_name} lessons with #{@student.first_name}"
else
@subject = "We had a problem paying you #{@distribution.real_distribution_display} for your lesson with #{@student.first_name}"
end
end
unique_args = {:type => "teacher_distribution_fail"} unique_args = {:type => "teacher_distribution_fail"}
sendgrid_category "Notification" sendgrid_category "Notification"
sendgrid_unique_args :type => unique_args[:type] sendgrid_unique_args :type => unique_args[:type]
sendgrid_recipients([email]) sendgrid_recipients([email])
sendgrid_substitute('@USERID', [@teacher.id]) sendgrid_substitute('@USERID', [@payable_teacher.id])
mail(:to => email, :subject => @subject) do |format| mail(:to => email, :subject => @subject) do |format|
format.text format.text
@ -1274,6 +1369,42 @@ module JamRuby
end end
end end
# always goes to the school owner
def school_distribution_done(teacher_payment)
@school = teacher_payment.school
@teacher_payment = teacher_payment
@distribution = teacher_payment.teacher_distribution
@teacher = teacher_payment.teacher
@payable_teacher = @school.owner
@name = @payable_teacher.first_name || 'Anonymous'
@student = @distribution.student
email = @payable_teacher.email
if @distribution.is_test_drive?
@subject = "#{@teacher.name} has earned #{@distribution.real_distribution_display} for TestDrive lesson with #{@student.name}"
elsif @distribution.is_normal?
@subject = "#{@teacher.name} has earned #{@distribution.real_distribution_display} for a lesson with #{@student.name}"
elsif @distribution.is_monthly?
@subject = "#{@teacher.name} has earned #{@distribution.real_distribution_display} for #{@distribution.month_name} lessons with #{@student.name}"
else
@subject = "#{@teacher.name} has earned #{@distribution.real_distribution_display} for a lesson with #{@student.name}"
end
unique_args = {:type => "school_distribution_done"}
sendgrid_category "Notification"
sendgrid_unique_args :type => unique_args[:type]
sendgrid_recipients([email])
sendgrid_substitute('@USERID', [@payable_teacher.id])
mail(:to => email, :subject => @subject) do |format|
format.text
format.html
end
end
def monthly_recurring_done(lesson_session) def monthly_recurring_done(lesson_session)
@student = lesson_session.student @student = lesson_session.student
@teacher = lesson_session.teacher @teacher = lesson_session.teacher
@ -1485,5 +1616,78 @@ module JamRuby
format.html format.html
end end
end end
def lesson_chat(chat_msg)
@target = chat_msg.target_user
@sender = chat_msg.user
@message = chat_msg.message
@lesson_session = chat_msg.lesson_session
@session_name = @lesson_session.music_session.name
@session_description = @lesson_session.music_session.description
@session_date = @lesson_session.slot.pretty_scheduled_start(true)
email = @target.email
@subject = "#{@sender.name} has sent you a message about a lesson"
unique_args = {:type => "lesson_chat"}
sendgrid_category "Notification"
sendgrid_unique_args :type => unique_args[:type]
sendgrid_recipients([email])
sendgrid_substitute('@USERID', [@target.id])
mail(:to => email, :subject => @subject) do |format|
format.text
format.html { render :layout => "from_user_mailer" }
end
end
def lesson_starting_soon_teacher(lesson_session)
@lesson_booking = lesson_booking = lesson_session.lesson_booking
@student = lesson_booking.student
@teacher = lesson_booking.teacher
@lesson_session = lesson_booking.next_lesson
@session_name = @lesson_session.music_session.name
@session_description = @lesson_session.music_session.description
@session_date = @lesson_session.slot.pretty_scheduled_start(true)
email = @teacher.email
@subject = "Your lesson with #{@student.first_name} on JamKazam is starting soon"
unique_args = {:type => "send_starting_notice_teacher"}
sendgrid_category "Notification"
sendgrid_unique_args :type => unique_args[:type]
sendgrid_recipients([email])
sendgrid_substitute('@USERID', [@teacher.id])
mail(:to => email, :subject => @subject) do |format|
format.text
format.html { render :layout => "from_user_mailer" }
end
end
def lesson_starting_soon_student(lesson_session)
@lesson_booking = lesson_booking = lesson_session.lesson_booking
@student = lesson_booking.student
@teacher = lesson_booking.teacher
@message = message
@lesson_session = lesson_booking.next_lesson
@session_name = @lesson_session.music_session.name
@session_description = @lesson_session.music_session.description
@session_date = @lesson_session.slot.pretty_scheduled_start(true)
email = @student.email
@subject = "Your lesson with #{@student.first_name} on JamKazam is starting soon"
unique_args = {:type => "send_starting_notice_student"}
sendgrid_category "Notification"
sendgrid_unique_args :type => unique_args[:type]
sendgrid_recipients([email])
sendgrid_substitute('@USERID', [@student.id])
mail(:to => email, :subject => @subject) do |format|
format.text
format.html { render :layout => "from_user_mailer" }
end
end
end end
end end

View File

@ -0,0 +1,18 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @sender.resolved_photo_url) %>
<% content_for :note do %>
<p>
<% if @message.present? %>
<br/><br/><%= @sender.name %> says:
<br/><%= @message %>
<br/>
<% end %>
<br/><br/>Click the button below to view the entire lesson conversation.</p>
<p>
<a href="<%= @lesson_session.chat_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON CONVERSATION</a>
</p>
<% end %>

View File

@ -0,0 +1,7 @@
<%= @subject %>
<%= @sender.name %> says:
<%= @message %>
To see the full lesson conversation, click here: <%= @lesson_session.chat_url %>

View File

@ -0,0 +1,12 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Your lesson with <%= @teacher.name %> is scheduled to begin on JamKazam in less than 30 minutes.
<p>
<a href="<%= @lesson_session.home_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">JAMCLASS HOME</a>
</p>
<% end %>

View File

@ -0,0 +1,4 @@
Your lesson with <%= @teacher.name %> is scheduled to begin on JamKazam in less than 30 minutes.
JAMCLASS HOME (<%= @lesson_session.home_url %>

View File

@ -0,0 +1,12 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @student.resolved_photo_url) %>
<% content_for :note do %>
<p>
Your lesson with <%= @student.name %> is scheduled to begin on JamKazam in less than 30 minutes.
<p>
<a href="<%= @lesson_session.home_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">JAMCLASS HOME</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
Your lesson with <%= @student.name %> is scheduled to begin on JamKazam in less than 30 minutes.
JAMCLASS HOME (<%= @lesson_session.home_url %>

View File

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

View File

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

View File

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

View File

@ -0,0 +1,39 @@
<% provide(:title, @subject) %>
<% if !@user.anonymous? %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<% end %>
<p>
Thank you for expressing an interest in exploring our music school partner program! A member of our staff will reach out to you shortly to chat with you and answer any/all questions you may have about our partner program, our technologies, and how we can help you continue to build your music school business.
</p>
<p>
We'd also like to provide links to some help articles that explain how many things work, and will likely answer many of your questions in a well-organized manner:
</p>
<p><a href="https://jamkazam.desk.com/customer/en/portal/topics/935633-jamclass-online-music-lessons---for-music-schools/articles" style="color:#fc0"><b style="color:#fc0">Guide for Music School Owners</b></a><br/>
These help articles explain things from the perspective of the school owner - e.g. how you can schedule and book lessons from our marketplace with your teachers, how billing and payments are handled, and so on.
</p>
<p><a href="https://jamkazam.desk.com/customer/en/portal/topics/926076-jamclass-online-music-lessons---for-teachers/articles" style="color:#fc0"><b style="color: #fc0">Guide for Music Lesson Teachers</b></a><br/>
These help articles explain how teachers use the features of the platform outside of the online lesson sessions.
</p>
<p><a href="https://jamkazam.desk.com/customer/en/portal/topics/673198-key-features-to-use-in-online-sessions/articles" style="color:#fc0"><b style="color: #fc0">Key Features To Use In Online Sessions</b></a><br/>
These help articles explain the key features instructors can use in online sessions to teach effectively.
</p>
<p>
<a href="https://jamkazam.desk.com/customer/en/portal/articles/1288274-computer-internet-audio-and-video-requirements"><b>Gear Requirements</b></a><br/>
This help article explains the requirements for your computer, audio and video gear, and Internet service.
</p>
<p>
Thanks again for connecting with us, and we look forward to speaking with you soon!
</p>
<p>Best Regards,<br/>
Team JamKazam</p>

View File

@ -0,0 +1,24 @@
<% if !@user.anonymous? %>
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %>
<% end %>
Thank you for expressing an interest in exploring our music school partner program! A member of our staff will reach out to you shortly to chat with you and answer any/all questions you may have about our partner program, our technologies, and how we can help you continue to build your music school business.
We'd also like to provide links to some help articles that explain how many things work, and will likely answer many of your questions in a well-organized manner:
-- Guide for Music School Owners (https://jamkazam.desk.com/customer/en/portal/topics/935633-jamclass-online-music-lessons---for-music-schools/articles)
These help articles explain things from the perspective of the school owner - e.g. how you can schedule and book lessons from our marketplace with your teachers, how billing and payments are handled, and so on.
-- Guide for Music Lesson Teachers (https://jamkazam.desk.com/customer/en/portal/topics/926076-jamclass-online-music-lessons---for-teachers/articles)
These help articles explain how teachers use the features of the platform outside of the online lesson sessions.
-- Key Features To Use In Online Sessions (https://jamkazam.desk.com/customer/en/portal/topics/673198-key-features-to-use-in-online-sessions/articles)
These help articles explain the key features instructors can use in online sessions to teach effectively.
-- Gear Requirements (https://jamkazam.desk.com/customer/en/portal/articles/1288274-computer-internet-audio-and-video-requirements)
This help article explains the requirements for your computer, audio and video gear, and Internet service.
Thanks again for connecting with us, and we look forward to speaking with you soon!
Best Regards,
Team JamKazam

View File

@ -14,7 +14,7 @@
<br/><%= @message %> <br/><%= @message %>
<br/> <br/>
<% end %> <% end %>
<br/><br/>Click the button below to get more information and to add this lesson to your calendar! <!--<br/><br/>Click the button below to get more information and to add this lesson to your calendar!-->
<br/><br/>We strongly suggest adding this to your calendar so you don't forget it, or you'll end up paying for a lesson you don't get.</p> <br/><br/>We strongly suggest adding this to your calendar so you don't forget it, or you'll end up paying for a lesson you don't get.</p>
<p> <p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a> <a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a>

View File

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

View File

@ -6,7 +6,7 @@
Hello <%= @student.name %>, Hello <%= @student.name %>,
</p> </p>
<p>You will not be billed for today's session with <%= @teacher.name %> <p>You will not be billed for today's session with <%= @teacher.name %>.
<br/> <br/>
<br/> <br/>
Click the button below to see more information about this session. Click the button below to see more information about this session.

View File

@ -1,5 +1,5 @@
Hello <%= @student.name %>, Hello <%= @student.name %>,
You will not be billed for today's session with <%= @teacher.name %> You will not be billed for today's session with <%= @teacher.name %>.
To see this lesson, click here: <%= @lesson_session.web_url %> To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -8,4 +8,4 @@
<%= @session_date %> <%= @session_date %>
</p> </p>
<p><a style="color: #ffcc00;" href="<%= @session_url %>">VIEW LESSON DETAILS</a></p> <p><a style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;"href="<%= @session_url %>">VIEW LESSON DETAILS</a></p>

View File

@ -1,4 +1,4 @@
<% provide(:title, "You have used #{@student.remaining_test_drives} of 4 TestDrive lesson credits") %> <% provide(:title, "You have used #{@student.remaining_test_drives} of #{@student.total_test_drives} TestDrive lesson credits") %>
<% provide(:photo_url, @teacher.resolved_photo_url) %> <% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %> <% content_for :note do %>

View File

@ -1,4 +1,4 @@
You have used <%= @student.remaining_test_drives %> of 4 TestDrive lesson credits. You have used <%= @student.remaining_test_drives %> of <%= @student.total_test_drives %> TestDrive lesson credits.
<% if @student.has_rated_teacher(@teacher) %> <% if @student.has_rated_teacher(@teacher) %>
Also, please rate your teacher at <%= @teacher.ratings_url %> now for todays lesson to help other students in the community find the best instructors. Also, please rate your teacher at <%= @teacher.ratings_url %> now for todays lesson to help other students in the community find the best instructors.

View File

@ -1,11 +1,11 @@
<% provide(:title, "You have used all 4 TestDrive lesson credits") %> <% provide(:title, "You have used all TestDrive lesson credits") %>
<p> <p>
Hello <%= @student.name %>, Hello <%= @student.name %>,
</p> </p>
<p> <p>
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. You have now used all 4 TestDrive credits. We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. You have now used all your TestDrive credits.
<% if !@student.has_rated_teacher(@teacher) %> <% if !@student.has_rated_teacher(@teacher) %>
Please <a href="<%= @teacher.ratings_url %>" style="color:#fc0">rate your teacher</a> now for todays lesson to Please <a href="<%= @teacher.ratings_url %>" style="color:#fc0">rate your teacher</a> now for todays lesson to

View File

@ -1,4 +1,4 @@
You have used all of your 4 TestDrive lesson credits. You have used all of your TestDrive lesson credits.
<% if @student.has_rated_teacher(@teacher) %> <% if @student.has_rated_teacher(@teacher) %>
Also, please rate your teacher at <%= @teacher.ratings_url %> now for todays lesson to help other students in the community find the best instructors. Also, please rate your teacher at <%= @teacher.ratings_url %> now for todays lesson to help other students in the community find the best instructors.

View File

@ -0,0 +1,23 @@
<% provide(:title, "Your TestDrive with #{@teacher.name} will not be billed") %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>You have not used a credit for today's TestDrive with <%= @teacher.name %>.
<br/>
<br/>
Click the button below to see more information about this session.
</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW
LESSON DETAILS</a>
</p>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,5 @@
Hello <%= @student.name %>,
You have not used a credit for today's TestDrive with <%= @teacher.name %>.
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -32,7 +32,7 @@
teacher for you. Finding the right teacher is the single most important determinant of success in your lessons. Would teacher for you. Finding the right teacher is the single most important determinant of success in your lessons. Would
you marry the first person you ever dated? No? Same here. Pick 4 teachers who look great, and then see who you click you marry the first person you ever dated? No? Same here. Pick 4 teachers who look great, and then see who you click
with. It's a phenomenal value, and then you can stick with the best teacher for you. with. It's a phenomenal value, and then you can stick with the best teacher for you.
<a href="https://www.jamkazam.com/client#/jamclass/book-lesson/purchase_test-drive" style="color:#fc0">Click this link <a href="https://www.jamkazam.com/client#/jamclass/lesson-payment/test-drive" style="color:#fc0">Click this link
to sign up now to sign up now
for TestDrive</a>. Then you can book 4 TestDrive lessons to get rolling. for TestDrive</a>. Then you can book 4 TestDrive lessons to get rolling.
</p> </p>

View File

@ -18,7 +18,7 @@ TestDrive lets you take 4 full lessons (30 minutes each) from 4 different teache
teacher for you. Finding the right teacher is the single most important determinant of success in your lessons. Would teacher for you. Finding the right teacher is the single most important determinant of success in your lessons. Would
you marry the first person you ever dated? No? Same here. Pick 4 teachers who look great, and then see who you click you marry the first person you ever dated? No? Same here. Pick 4 teachers who look great, and then see who you click
with. It's a phenomenal value, and then you can stick with the best teacher for you. with. It's a phenomenal value, and then you can stick with the best teacher for you.
Click this link to sign up now for TestDrive (https://www.jamkazam.com/client#/jamclass/book-lesson/purchase_test-drive). Click this link to sign up now for TestDrive (https://www.jamkazam.com/client#/jamclass/test-drive-selection).
Then you can book 4 TestDrive lessons to get rolling. Then you can book 4 TestDrive lessons to get rolling.
2. Set Up Your Gear 2. Set Up Your Gear

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@
<% else %> <% else %>
This student has accepted your lesson request! This student has accepted your lesson request!
<% end %> <% end %>
<br/><br/>Click the button below to get more information and to add this lesson to your calendar! <!--<br/><br/>Click the button below to get more information and to add this lesson to your calendar!-->
<br/><br/>We strongly suggest adding this to your calendar so you don't forget it.</p> <br/><br/>We strongly suggest adding this to your calendar so you don't forget it.</p>
<p> <p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a> <a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a>

View File

@ -8,4 +8,4 @@
<%= @session_date %> <%= @session_date %>
</p> </p>
<p><a style="color: #ffcc00;" href="<%= @session_url %>">VIEW LESSON DETAILS</a></p> <p><a style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;"href="<%= @session_url %>">VIEW LESSON DETAILS</a></p>

View File

@ -419,7 +419,7 @@ SQL
end end
connection.join_the_session(music_session, as_musician, tracks, user, audio_latency, video_sources) connection.join_the_session(music_session, as_musician, tracks, user, audio_latency, video_sources)
JamRuby::MusicSessionUserHistory.join_music_session(user.id, music_session.id) JamRuby::MusicSessionUserHistory.join_music_session(user.id, music_session.id, client_id)
# connection.music_session_id = music_session.id # connection.music_session_id = music_session.id
# connection.as_musician = as_musician # connection.as_musician = as_musician
# connection.joining_session = true # connection.joining_session = true

View File

@ -1,5 +1,5 @@
require 'influxdb' #require 'influxdb'
# monkey patch InfluxDB client to clear the queue when asked to stop # monkey patch InfluxDB client to clear the queue when asked to stop
=begin =begin

View File

@ -993,14 +993,16 @@ module JamRuby
end end
# creates the chat message # creates the chat message
def chat_message(session_id, sender_name, sender_id, msg, msg_id, created_at, channel) def chat_message(session_id, sender_name, sender_id, msg, msg_id, created_at, channel, lesson_session_id, purpose)
chat_message = Jampb::ChatMessage.new( chat_message = Jampb::ChatMessage.new(
:sender_id => sender_id, :sender_id => sender_id,
:sender_name => sender_name, :sender_name => sender_name,
:msg => msg, :msg => msg,
:msg_id => msg_id, :msg_id => msg_id,
:created_at => created_at, :created_at => created_at,
:channel => channel :channel => channel,
:lesson_session_id => lesson_session_id,
:purpose => purpose
) )
if session_id if session_id

View File

@ -843,5 +843,9 @@ module JamRuby
stats stats
end end
def lesson_session
music_session.lesson_session
end
end end
end end

View File

@ -357,7 +357,7 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
UPDATE affiliate_quarterly_payments UPDATE affiliate_quarterly_payments
SET SET
closed = TRUE, closed_at = NOW() closed = TRUE, closed_at = NOW()
WHERE year < #{year} OR quarter < #{quarter} WHERE year < #{year} OR (year = #{year} AND quarter < #{quarter})
} }
ActiveRecord::Base.connection.execute(sql) ActiveRecord::Base.connection.execute(sql)

View File

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

View File

@ -4,7 +4,7 @@ module JamRuby
DEFAULT_ENVIRONMENT = 'public' DEFAULT_ENVIRONMENT = 'public'
CLIENT_PREFIX = 'JamClient' CLIENT_PREFIX = 'JamClient'
PRODUCTS = ["#{CLIENT_PREFIX}/Win32", "#{CLIENT_PREFIX}/MacOSX"] PRODUCTS = ["#{CLIENT_PREFIX}/Win32", "#{CLIENT_PREFIX}/MacOSX", "#{CLIENT_PREFIX}/JamBlaster"]
self.primary_key = 'id' self.primary_key = 'id'
attr_accessible :version, :uri, :sha1, :environment, :product, as: :admin attr_accessible :version, :uri, :sha1, :environment, :product, as: :admin

View File

@ -1,6 +1,8 @@
module JamRuby module JamRuby
class Charge < ActiveRecord::Base class Charge < ActiveRecord::Base
belongs_to :user, class_name: "JamRuby::User"
validates :sent_billing_notices, inclusion: {in: [true, false]} validates :sent_billing_notices, inclusion: {in: [true, false]}
def max_retries def max_retries
@ -96,6 +98,7 @@ module JamRuby
else else
self.billing_error_detail = e.to_s + "\n" + e.backtrace.join("\n\t") if e.backtrace self.billing_error_detail = e.to_s + "\n" + e.backtrace.join("\n\t") if e.backtrace
end end
puts "Charge: unhandled exception #{billing_error_reason}, #{billing_error_detail}"
self.save(validate: false) self.save(validate: false)
end end

View File

@ -16,26 +16,44 @@ module JamRuby
belongs_to :user belongs_to :user
belongs_to :music_session belongs_to :music_session
belongs_to :target_user, class_name: "JamRuby::User" belongs_to :target_user, class_name: "JamRuby::User"
belongs_to :lesson_booking, class_name: "JamRuby::LessonBooking" belongs_to :lesson_session, class_name: "JamRuby::LessonSession"
validates :user, presence: true validates :user, presence: true
validates :message, length: {minimum: 1, maximum: 255}, no_profanity: true, unless: :ignore_message_checks validates :message, length: {minimum: 1, maximum: 255}, no_profanity: true, unless: :ignore_message_checks
def self.create(user, music_session, message, channel, client_id, target_user = nil, lesson_booking = nil) def self.create(user, music_session, message, channel, client_id, target_user = nil, lesson_session = nil, purpose = nil)
chat_msg = ChatMessage.new chat_msg = ChatMessage.new
chat_msg.user_id = user.id chat_msg.user_id = user.id
chat_msg.music_session_id = music_session.id if music_session chat_msg.music_session_id = music_session.id if music_session
chat_msg.message = message chat_msg.message = message
chat_msg.channel = channel chat_msg.channel = channel
chat_msg.target_user = target_user chat_msg.target_user = target_user
chat_msg.lesson_booking = lesson_booking chat_msg.lesson_session = lesson_session
chat_msg.purpose = purpose
if lesson_booking
if lesson_session
chat_msg.ignore_message_checks = true chat_msg.ignore_message_checks = true
if user.id == lesson_session.student.id
lesson_session.teacher_unread_messages = true
Notification.send_lesson_message('chat', lesson_session, false, message)
else
lesson_session.student_unread_messages = true
Notification.send_lesson_message('chat', lesson_session, true, message)
end
lesson_session.save(validate: false)
# a nil purpose means 'normal chat', which is the only time we should send an email
if !target_user.online? && purpose.nil? && message.present?
UserMailer.lesson_chat(chat_msg).deliver!
end
end end
if chat_msg.save if chat_msg.save
ChatMessage.send_chat_msg music_session, chat_msg, user, client_id, channel ChatMessage.send_chat_msg music_session, chat_msg, user, client_id, channel, lesson_session, purpose, target_user
end end
chat_msg chat_msg
end end
@ -60,6 +78,11 @@ module JamRuby
query = ChatMessage.where('music_session_id = ?', music_session_id) query = ChatMessage.where('music_session_id = ?', music_session_id)
end end
if params.has_key? (:lesson_session)
lesson_session_id = params[:lesson_session]
query = ChatMessage.where('lesson_session_id = ?', lesson_session_id)
end
query = query.offset(start).limit(limit).order('created_at DESC').includes([:user]) query = query.offset(start).limit(limit).order('created_at DESC').includes([:user])
if query.length == 0 if query.length == 0
@ -71,8 +94,9 @@ module JamRuby
end end
end end
def send_chat_msg(music_session, chat_msg, user, client_id, channel) def send_chat_msg(music_session, chat_msg, user, client_id, channel, lesson_session, purpose, target_user)
music_session_id = music_session.id if music_session music_session_id = music_session.id if music_session
lesson_session_id = lesson_session.id if lesson_session
msg = @@message_factory.chat_message( msg = @@message_factory.chat_message(
music_session_id, music_session_id,
@ -81,14 +105,19 @@ module JamRuby
chat_msg.message, chat_msg.message,
chat_msg.id, chat_msg.id,
chat_msg.created_at.utc.iso8601, chat_msg.created_at.utc.iso8601,
channel channel,
lesson_session_id,
purpose
) )
if channel == 'session' if channel == 'session'
@@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id}) @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id})
elsif channel == 'global' elsif channel == 'global'
@@mq_router.publish_to_active_clients(msg) @@mq_router.publish_to_active_clients(msg)
elsif channel == 'lesson'
@@mq_router.publish_to_user(target_user.id, msg, sender = {:client_id => client_id})
end end
end end
end end
end end

View File

@ -47,7 +47,7 @@ module JamRuby
has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_instruments" has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_instruments"
def self.standard_list def self.standard_list
return Instrument.where('instruments.popularity > 0').order('instruments.popularity DESC, instruments.description ASC') return Instrument.where('instruments.popularity > 0').order('instruments.description ASC')
end end
def self.jam_track_list def self.jam_track_list

View File

@ -3,5 +3,10 @@ module JamRuby
include HtmlSanitize include HtmlSanitize
html_sanitize strict: [:name, :description] html_sanitize strict: [:name, :description]
has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_languages" has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_languages"
def self.english_sort
languages = Language.order(:description)
languages.sort_by { |l| [ l.id == 'EN' ? 0 : 1, l.description] }
end
end end
end end

View File

@ -9,7 +9,7 @@ module JamRuby
@@log = Logging.logger[LessonBooking] @@log = Logging.logger[LessonBooking]
attr_accessor :accepting, :countering, :countered_slot, :countered_lesson attr_accessor :accepting, :countering, :canceling, :countered_slot, :countered_lesson
STATUS_REQUESTED = 'requested' STATUS_REQUESTED = 'requested'
STATUS_CANCELED = 'canceled' STATUS_CANCELED = 'canceled'
@ -37,12 +37,12 @@ module JamRuby
belongs_to :teacher, class_name: "JamRuby::User" belongs_to :teacher, class_name: "JamRuby::User"
belongs_to :accepter, class_name: "JamRuby::User" belongs_to :accepter, class_name: "JamRuby::User"
belongs_to :canceler, class_name: "JamRuby::User" belongs_to :canceler, class_name: "JamRuby::User"
belongs_to :default_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :default_slot_id, inverse_of: :defaulted_booking belongs_to :default_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :default_slot_id, inverse_of: :defaulted_booking, :dependent => :destroy
belongs_to :counter_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :counter_slot_id, inverse_of: :countered_booking belongs_to :counter_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :counter_slot_id, inverse_of: :countered_booking, :dependent => :destroy
belongs_to :school, class_name: "JamRuby::School"
has_many :lesson_booking_slots, class_name: "JamRuby::LessonBookingSlot" has_many :lesson_booking_slots, class_name: "JamRuby::LessonBookingSlot", :dependent => :destroy
has_many :lesson_sessions, class_name: "JamRuby::LessonSession" has_many :lesson_sessions, class_name: "JamRuby::LessonSession", :dependent => :destroy
has_many :lesson_package_purchases, class_name: "JamRuby::LessonPackagePurchase" has_many :lesson_package_purchases, class_name: "JamRuby::LessonPackagePurchase", :dependent => :destroy
validates :user, presence: true validates :user, presence: true
validates :teacher, presence: true validates :teacher, presence: true
@ -62,7 +62,9 @@ module JamRuby
validate :validate_lesson_booking_slots validate :validate_lesson_booking_slots
validate :validate_lesson_length validate :validate_lesson_length
validate :validate_payment_style validate :validate_payment_style
validate :validate_uncollectables, on: :create
validate :validate_accepted, :if => :accepting validate :validate_accepted, :if => :accepting
validate :validate_canceled, :if => :canceling
before_save :before_save before_save :before_save
@ -85,7 +87,7 @@ module JamRuby
end end
def after_create def after_create
if card_presumed_ok && !sent_notices if (card_presumed_ok || school_on_school?) && !sent_notices
send_notices send_notices
end end
end end
@ -136,6 +138,9 @@ module JamRuby
def next_lesson def next_lesson
if recurring if recurring
session = lesson_sessions.joins(:music_session).where("scheduled_start is not null").where("scheduled_start > ?", Time.now).order(:created_at).first session = lesson_sessions.joins(:music_session).where("scheduled_start is not null").where("scheduled_start > ?", Time.now).order(:created_at).first
if session.nil?
session = lesson_sessions[0]
end
LessonSession.find(session.id) if session LessonSession.find(session.id) if session
else else
lesson_sessions[0] lesson_sessions[0]
@ -272,7 +277,7 @@ module JamRuby
times << time times << time
end end
end end
times { times: times, session: sessions.first }
end end
def determine_needed_sessions(sessions) def determine_needed_sessions(sessions)
@ -390,6 +395,14 @@ module JamRuby
self.accepting = false self.accepting = false
end end
def validate_canceled
if !is_canceled?
self.errors.add(:status, "This session is already #{self.status}.")
end
self.canceling = false
end
def send_notices def send_notices
UserMailer.student_lesson_request(self).deliver UserMailer.student_lesson_request(self).deliver
UserMailer.teacher_lesson_request(self).deliver UserMailer.teacher_lesson_request(self).deliver
@ -398,11 +411,26 @@ module JamRuby
self.save self.save
end end
def resolved_test_drive_package
result = nil
purchase = student.most_recent_test_drive_purchase
if purchase
# for lessons already packaged
result = purchase.lesson_package_type
else
# for unbooked lessons
result = student.desired_package
end
if result.nil?
result = LessonPackageType.test_drive_4
end
result
end
def lesson_package_type def lesson_package_type
if is_single_free? if is_single_free?
LessonPackageType.single_free LessonPackageType.single_free
elsif is_test_drive? elsif is_test_drive?
LessonPackageType.test_drive resolved_test_drive_package
elsif is_normal? elsif is_normal?
LessonPackageType.single LessonPackageType.single
end end
@ -439,19 +467,32 @@ module JamRuby
if is_single_free? if is_single_free?
0 0
elsif is_test_drive? elsif is_test_drive?
LessonPackageType.test_drive.price resolved_test_drive_package.price
elsif is_normal? elsif is_normal?
teacher.teacher.booking_price(lesson_length, payment_style != PAYMENT_STYLE_MONTHLY) teacher.teacher.booking_price(lesson_length, payment_style != PAYMENT_STYLE_MONTHLY)
end end
end end
def distribution_price_in_cents def distribution_price_in_cents(target)
if is_single_free? if is_single_free?
0 0
elsif is_test_drive? elsif is_test_drive?
10 * 100 10 * 100
elsif is_normal? elsif is_normal?
booked_price * 100 if is_monthly_payment?
raise "not a LessonPackagePurchase: #{target.inspect}" if !target.is_a?(LessonPackagePurchase)
today = Date.today
start_date = Date.new(target.year, target.month, 1)
if today.year == target.year && today.month == target.month
# we are in the month being billed. we should set the start date based on today
start_date = today
end
LessonSessionMonthlyPrice.price(self, start_date) * 100
else
booked_price * 100
end
end end
end end
@ -500,41 +541,49 @@ module JamRuby
def approved_before? def approved_before?
!self.accepter_id.nil? !self.accepter_id.nil?
end end
def cancel(canceler, other, message) def cancel(canceler, other, message)
self.canceling = true
self.active = false self.active = false
self.status = STATUS_CANCELED self.status = STATUS_CANCELED
self.cancel_message = message self.cancel_message = message
self.canceler = canceler self.canceler = canceler
success = save success = save
if success if success
lesson_sessions.past_cancel_window.each do |lesson_session|
lesson_session = LessonSession.find(lesson_session.id) # because .upcoming creates ReadOnly records
lesson_session.cancel_lesson(canceler, message)
if !lesson_session.save
return lesson_session
end
end
if approved_before? if approved_before?
# just tell both people it's cancelled, to act as confirmation # just tell both people it's cancelled, to act as confirmation
Notification.send_lesson_message('canceled', next_lesson, false) Notification.send_lesson_message('canceled', next_lesson, false)
Notification.send_lesson_message('canceled', next_lesson, true) Notification.send_lesson_message('canceled', next_lesson, true)
UserMailer.student_lesson_booking_canceled(self, message).deliver UserMailer.student_lesson_booking_canceled(self, message).deliver
UserMailer.teacher_lesson_booking_canceled(self, message).deliver UserMailer.teacher_lesson_booking_canceled(self, message).deliver
chat_message_prefix = "Lesson Canceled" purpose = "Lesson Canceled"
else else
if canceler == student if canceler == student
# if it's the first time acceptance student canceling, we call it a 'cancel' # if it's the first time acceptance student canceling, we call it a 'cancel'
Notification.send_lesson_message('canceled', next_lesson, false) Notification.send_lesson_message('canceled', next_lesson, false)
UserMailer.teacher_lesson_booking_canceled(self, message).deliver UserMailer.teacher_lesson_booking_canceled(self, message).deliver
chat_message_prefix = "Lesson Canceled" purpose = "Lesson Canceled"
else else
# if it's the first time acceptance teacher, it was declined # if it's the first time acceptance teacher, it was declined
UserMailer.student_lesson_booking_declined(self, message).deliver UserMailer.student_lesson_booking_declined(self, message).deliver
Notification.send_lesson_message('declined', next_lesson, true) Notification.send_lesson_message('declined', next_lesson, true)
chat_message_prefix = "Lesson Declined" purpose = "Lesson Declined"
end end
end end
chat_message = message.nil? ? chat_message_prefix : "#{chat_message_prefix}: #{message}" message = '' if message.nil?
msg = ChatMessage.create(canceler, nil, chat_message, ChatMessage::CHANNEL_LESSON, nil, other, self) msg = ChatMessage.create(canceler, nil, message, ChatMessage::CHANNEL_LESSON, nil, other, next_lesson, purpose)
end end
success self
end end
def card_approved def card_approved
@ -555,7 +604,7 @@ module JamRuby
#end #end
elsif is_test_drive? elsif is_test_drive?
if user.has_requested_test_drive?(teacher) && !user.admin if user.has_requested_test_drive?(teacher) && !user.admin
errors.add(:user, "has a requested TestDrive with this teacher") errors.add(:user, "have a requested TestDrive with this teacher")
end end
if !user.has_test_drives? && !user.can_buy_test_drive? if !user.has_test_drives? && !user.can_buy_test_drive?
errors.add(:user, "have no remaining test drives") errors.add(:user, "have no remaining test drives")
@ -603,6 +652,15 @@ module JamRuby
end end
end end
def validate_uncollectables
if user.uncollectables.count > 0
errors.add(:user, 'have unpaid lessons.')
end
end
def school_owned?
!!school
end
def self.book_free(user, teacher, lesson_booking_slots, description) def self.book_free(user, teacher, lesson_booking_slots, description)
self.book(user, teacher, LessonBooking::LESSON_TYPE_FREE, lesson_booking_slots, false, 30, PAYMENT_STYLE_ELSEWHERE, description) self.book(user, teacher, LessonBooking::LESSON_TYPE_FREE, lesson_booking_slots, false, 30, PAYMENT_STYLE_ELSEWHERE, description)
@ -632,6 +690,9 @@ module JamRuby
lesson_booking.payment_style = payment_style lesson_booking.payment_style = payment_style
lesson_booking.description = description lesson_booking.description = description
lesson_booking.status = STATUS_REQUESTED lesson_booking.status = STATUS_REQUESTED
if lesson_booking.teacher && lesson_booking.teacher.teacher.school
lesson_booking.school = lesson_booking.teacher.teacher.school
end
# two-way association slots, for before_validation loic in slot to work # two-way association slots, for before_validation loic in slot to work
lesson_booking.lesson_booking_slots = lesson_booking_slots lesson_booking.lesson_booking_slots = lesson_booking_slots
@ -641,20 +702,24 @@ module JamRuby
end if lesson_booking_slots end if lesson_booking_slots
if lesson_booking.save if lesson_booking.save
msg = ChatMessage.create(user, lesson_booking.lesson_sessions[0], description, ChatMessage::CHANNEL_LESSON, nil, teacher, lesson_booking) description = '' if description.nil?
msg = ChatMessage.create(user, lesson_booking.lesson_sessions[0], description, ChatMessage::CHANNEL_LESSON, nil, teacher, lesson_booking.lesson_sessions[0], 'Lesson Requested')
end end
end end
lesson_booking lesson_booking
end end
def self.unprocessed(current_user) def self.unprocessed(current_user)
LessonBooking.where(user_id: current_user.id).where(card_presumed_ok: false) LessonBooking.where(user_id: current_user.id).where(card_presumed_ok: false).where('school_id IS NULL')
end end
def self.requested(current_user) def self.requested(current_user)
LessonBooking.where(user_id: current_user.id).where(status: STATUS_REQUESTED) LessonBooking.where(user_id: current_user.id).where(status: STATUS_REQUESTED)
end end
def school_on_school?
teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id)
end
def self.find_bookings_needing_sessions(minimum_start_time) def self.find_bookings_needing_sessions(minimum_start_time)
MusicSession.select([:lesson_booking_id]).joins(:lesson_session => :lesson_booking).where("lesson_bookings.active = true").where('lesson_bookings.recurring = true').where("scheduled_start is not null").where("scheduled_start > ?", minimum_start_time).group(:lesson_booking_id).having('count(lesson_booking_id) < 2') MusicSession.select([:lesson_booking_id]).joins(:lesson_session => :lesson_booking).where("lesson_bookings.active = true").where('lesson_bookings.recurring = true').where("scheduled_start is not null").where("scheduled_start > ?", minimum_start_time).group(:lesson_booking_id).having('count(lesson_booking_id) < 2')
@ -806,7 +871,6 @@ module JamRuby
end end
end end
def home_url def home_url
APP_CONFIG.external_root_url + "/client#/jamclass" APP_CONFIG.external_root_url + "/client#/jamclass"
end end

View File

@ -73,25 +73,44 @@ module JamRuby
def scheduled_times(needed_sessions, minimum_start_time) def scheduled_times(needed_sessions, minimum_start_time)
#puts "NEEDED SESSIONS #{needed_sessions} #{minimum_start_time}"
times = [] times = []
week_offset = 0 week_offset = 0
needed_sessions.times do |i| needed_sessions.times do |i|
candidate = scheduled_time(i + week_offset) candidate = scheduled_time(i + week_offset)
#puts "#{i}: candidate #{candidate} week_offset:#{week_offset}"
if day_of_week && candidate <= minimum_start_time if day_of_week && candidate <= minimum_start_time
# move it up a week # move it up a week
week_offset += 1 week_offset += 1
candidate = scheduled_time(i + week_offset) candidate = scheduled_time(i + week_offset)
#puts "retry #1 #{candidate}"
# sanity check # sanity check
if candidate <= minimum_start_time if candidate <= minimum_start_time
week_offset += 1 week_offset += 1
candidate = scheduled_time(i + week_offset) candidate = scheduled_time(i + week_offset)
#puts "retry #2 #{candidate}"
if candidate <= minimum_start_time if candidate <= minimum_start_time
raise "candidate time less than minimum start time even after scoot: #{lesson_booking.id} #{self.id}"
week_offset += 1
candidate = scheduled_time(i + week_offset)
#puts "retry #3 #{candidate}"
if candidate <= minimum_start_time
week_offset += 1
candidate = scheduled_time(i + week_offset)
#puts "retry #4 #{candidate}"
if candidate <= minimum_start_time
raise "candidate time less than minimum start time even after scoot: #{lesson_booking.id} #{self.id}"
end
end
end end
end end
end end
times << candidate times << candidate

View File

@ -26,23 +26,26 @@ module JamRuby
def validate_test_drive def validate_test_drive
if user if user
if !user.can_buy_test_drive? 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") 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
def create_charge def create_charge
self.lesson_payment_charge = LessonPaymentCharge.new if !school_on_school? && lesson_booking && lesson_booking.is_monthly_payment?
lesson_payment_charge.amount_in_cents = 0 self.lesson_payment_charge = LessonPaymentCharge.new
lesson_payment_charge.fee_in_cents = 0 lesson_payment_charge.user = user
lesson_payment_charge.lesson_package_purchase = self lesson_payment_charge.amount_in_cents = 0
lesson_payment_charge.save! lesson_payment_charge.fee_in_cents = 0
lesson_payment_charge.lesson_package_purchase = self
lesson_payment_charge.save!
end
end end
def add_test_drives def add_test_drives
if self.lesson_package_type.is_test_drive? if self.lesson_package_type.is_test_drive?
new_test_drives = user.remaining_test_drives + 4 new_test_drives = user.remaining_test_drives + lesson_package_type.test_drive_count
User.where(id: user.id).update_all(remaining_test_drives: new_test_drives) User.where(id: user.id).update_all(remaining_test_drives: new_test_drives)
user.remaining_test_drives = new_test_drives user.remaining_test_drives = new_test_drives
end end
@ -71,17 +74,19 @@ module JamRuby
if lesson_booking && lesson_booking.requires_teacher_distribution?(purchase) if lesson_booking && lesson_booking.requires_teacher_distribution?(purchase)
purchase.teacher_distribution = TeacherDistribution.create_for_lesson_package_purchase(purchase) purchase.teacher_distribution = TeacherDistribution.create_for_lesson_package_purchase(purchase)
# price should always match the teacher_distribution, if there is one
purchase.price = purchase.teacher_distribution.amount_in_cents / 100
end end
else else
purchase.recurring = false purchase.recurring = false
end end
if lesson_booking if lesson_booking
purchase.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 # lesson_package_type.booked_price(lesson_booking) 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 purchase.price = lesson_package_type.price if purchase.price.nil?
end end
purchase.save purchase.save
@ -100,6 +105,9 @@ module JamRuby
description(lesson_booking) description(lesson_booking)
end end
def timed_description
"Lessons for the month of #{self.month_name} with #{self.lesson_booking.student.name}"
end
def month_name def month_name
if recurring if recurring
@ -113,11 +121,21 @@ module JamRuby
user user
end end
def school_on_school?
teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id)
end
def bill_monthly(force = false) def bill_monthly(force = false)
lesson_payment_charge.charge(force)
if lesson_payment_charge.billed if school_on_school?
success = true
else
lesson_payment_charge.charge(force)
success = lesson_payment_charge.billed
end
if success
self.sent_notices = true self.sent_notices = true
self.sent_notices_at = Time.now self.sent_notices_at = Time.now
self.post_processed = true self.post_processed = true

View File

@ -7,21 +7,29 @@ module JamRuby
PRODUCT_TYPE = 'LessonPackageType' PRODUCT_TYPE = 'LessonPackageType'
SINGLE_FREE = 'single-free' SINGLE_FREE = 'single-free'
TEST_DRIVE = 'test-drive' TEST_DRIVE_4 = 'test-drive'
TEST_DRIVE_2 = 'test-drive-2'
TEST_DRIVE_1 = 'test-drive-1'
SINGLE = 'single' SINGLE = 'single'
LESSON_PACKAGE_TYPES = LESSON_PACKAGE_TYPES =
[ [
SINGLE_FREE, SINGLE_FREE,
TEST_DRIVE, TEST_DRIVE_4,
TEST_DRIVE_2,
TEST_DRIVE_1,
SINGLE SINGLE
] ]
has_many :user_desired_packages, class_name: "JamRuby::User", :foreign_key => "lesson_package_type_id", inverse_of: :desired_package
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
[TEST_DRIVE_4, TEST_DRIVE_2, TEST_DRIVE_1]
end
def self.monthly def self.monthly
LessonPackageType.find(MONTHLY) LessonPackageType.find(MONTHLY)
end end
@ -30,8 +38,16 @@ module JamRuby
LessonPackageType.find(SINGLE_FREE) LessonPackageType.find(SINGLE_FREE)
end end
def self.test_drive def self.test_drive_4
LessonPackageType.find(TEST_DRIVE) LessonPackageType.find(TEST_DRIVE_4)
end
def self.test_drive_2
LessonPackageType.find(TEST_DRIVE_2)
end
def self.test_drive_1
LessonPackageType.find(TEST_DRIVE_1)
end end
def self.single def self.single
@ -42,17 +58,21 @@ module JamRuby
if is_single_free? if is_single_free?
0 0
elsif is_test_drive? elsif is_test_drive?
LessonPackageType.test_drive.price 10.00
elsif is_normal? elsif is_normal?
lesson_booking.booked_price #teacher.teacher.booking_price(lesson_booking.lesson_length, lesson_booking.payment_style == LessonBooking::PAYMENT_STYLE_SINGLE) lesson_booking.booked_price #teacher.teacher.booking_price(lesson_booking.lesson_length, lesson_booking.payment_style == LessonBooking::PAYMENT_STYLE_SINGLE)
end end
end end
def test_drive_count
package_type["test-drive-".length, 1].to_i
end
def description(lesson_booking) def description(lesson_booking)
if is_single_free? if is_single_free?
"Single Free Lesson" "Single Free Lesson"
elsif is_test_drive? elsif is_test_drive?
"Test Drive" "Test Drive (#{test_drive_count})"
elsif is_normal? elsif is_normal?
if lesson_booking.recurring if lesson_booking.recurring
"Recurring #{lesson_booking.payment_style == LessonBooking::PAYMENT_STYLE_WEEKLY ? "Weekly" : "Monthly"} #{lesson_booking.lesson_length}m" "Recurring #{lesson_booking.payment_style == LessonBooking::PAYMENT_STYLE_WEEKLY ? "Weekly" : "Monthly"} #{lesson_booking.lesson_length}m"
@ -71,7 +91,7 @@ module JamRuby
end end
def is_test_drive? def is_test_drive?
id == TEST_DRIVE id.start_with?('test-drive')
end end
def is_normal? def is_normal?
@ -86,8 +106,12 @@ 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 elsif package_type == 'test-drive-4'
"lesson-package-test-drive" "lesson-package-test-drive-4"
elsif package_type == TEST_DRIVE_2
"lesson-package-test-drive-2"
elsif package_type == TEST_DRIVE_1
"lesson-package-test-drive-1"
elsif package_type == SINGLE elsif package_type == SINGLE
"lesson-package-single" "lesson-package-single"
else else

View File

@ -9,7 +9,7 @@ module JamRuby
end end
def charged_user def charged_user
@charged_user ||= target.student user
end end
def resolve_target def resolve_target
@ -31,6 +31,10 @@ module JamRuby
charged_user charged_user
end end
def teacher
target.teacher
end
def is_lesson? def is_lesson?
!lesson_session.nil? !lesson_session.nil?
end end
@ -85,5 +89,13 @@ module JamRuby
end end
end end
end end
def description
target.timed_description
end
def expected_price_in_cents
target.lesson_booking.distribution_price_in_cents(target)
end
end end
end end

View File

@ -5,13 +5,13 @@ module JamRuby
include HtmlSanitize include HtmlSanitize
html_sanitize strict: [:cancel_message] html_sanitize strict: [:cancel_message]
attr_accessor :accepting, :creating, :countering, :countered_slot, :countered_lesson, :canceling attr_accessor :accepting, :creating, :countering, :countered_slot, :countered_lesson, :canceling, :assigned_student
@@log = Logging.logger[LessonSession] @@log = Logging.logger[LessonSession]
delegate :sent_billing_notices, :last_billing_attempt_at, :billing_attempts, :billing_should_retry, :billed, :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 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?, to: :lesson_booking delegate :is_test_drive?, :is_single_free?, :is_normal?, :approved_before?, :is_active?, :recurring, :is_monthly_payment?, to: :lesson_booking
delegate :pretty_scheduled_start, to: :music_session delegate :pretty_scheduled_start, to: :music_session
@ -30,16 +30,18 @@ module JamRuby
LESSON_TYPE_TEST_DRIVE = 'test-drive' LESSON_TYPE_TEST_DRIVE = 'test-drive'
LESSON_TYPES = [LESSON_TYPE_SINGLE, LESSON_TYPE_SINGLE_FREE, LESSON_TYPE_TEST_DRIVE] LESSON_TYPES = [LESSON_TYPE_SINGLE, LESSON_TYPE_SINGLE_FREE, LESSON_TYPE_TEST_DRIVE]
has_one :music_session, class_name: "JamRuby::MusicSession" has_one :music_session, class_name: "JamRuby::MusicSession", :dependent => :destroy
belongs_to :teacher, class_name: "JamRuby::User", foreign_key: :teacher_id, inverse_of: :taught_lessons belongs_to :teacher, class_name: "JamRuby::User", foreign_key: :teacher_id, inverse_of: :taught_lessons
belongs_to :canceler, class_name: "JamRuby::User", foreign_key: :canceler_id belongs_to :canceler, class_name: "JamRuby::User", foreign_key: :canceler_id
belongs_to :lesson_package_purchase, class_name: "JamRuby::LessonPackagePurchase" belongs_to :lesson_package_purchase, class_name: "JamRuby::LessonPackagePurchase"
belongs_to :lesson_booking, class_name: "JamRuby::LessonBooking" belongs_to :lesson_booking, class_name: "JamRuby::LessonBooking"
belongs_to :slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :slot_id belongs_to :slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :slot_id, :dependent => :destroy
belongs_to :lesson_payment_charge, class_name: "JamRuby::LessonPaymentCharge", foreign_key: :charge_id belongs_to :lesson_payment_charge, class_name: "JamRuby::LessonPaymentCharge", foreign_key: :charge_id
belongs_to :counter_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :counter_slot_id, inverse_of: :countered_lesson belongs_to :counter_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :counter_slot_id, inverse_of: :countered_lesson, :dependent => :destroy
has_one :teacher_distribution, class_name: "JamRuby::TeacherDistribution" has_one :teacher_distribution, class_name: "JamRuby::TeacherDistribution"
has_many :lesson_booking_slots, class_name: "JamRuby::LessonBookingSlot" has_many :lesson_booking_slots, class_name: "JamRuby::LessonBookingSlot"
has_many :notifications, :class_name => "JamRuby::Notification", :foreign_key => "lesson_session_id"
has_many :chat_messages, :class_name => "JamRuby::ChatMessage", :foreign_key => "lesson_session_id"
validates :duration, presence: true, numericality: {only_integer: true} validates :duration, presence: true, numericality: {only_integer: true}
@ -69,10 +71,13 @@ module JamRuby
scope :suspended, -> { where(status: STATUS_SUSPENDED) } scope :suspended, -> { where(status: STATUS_SUSPENDED) }
scope :completed, -> { where(status: STATUS_COMPLETED) } scope :completed, -> { where(status: STATUS_COMPLETED) }
scope :missed, -> { where(status: STATUS_MISSED) } scope :missed, -> { where(status: STATUS_MISSED) }
scope :upcoming, -> { joins(:music_session).where('music_sessions.scheduled_start > ?', Time.now) }
scope :past_cancel_window, -> { joins(:music_session).where('music_sessions.scheduled_start > ?', 24.hours.from_now) }
def create_charge def create_charge
if !is_test_drive? if !school_on_school? && !is_test_drive? && !is_monthly_payment?
self.lesson_payment_charge = LessonPaymentCharge.new self.lesson_payment_charge = LessonPaymentCharge.new
lesson_payment_charge.user = @assigned_student
lesson_payment_charge.amount_in_cents = 0 lesson_payment_charge.amount_in_cents = 0
lesson_payment_charge.fee_in_cents = 0 lesson_payment_charge.fee_in_cents = 0
lesson_payment_charge.lesson_session = self lesson_payment_charge.lesson_session = self
@ -96,23 +101,52 @@ module JamRuby
self.save self.save
end end
def music_session_id
music_session.id
end
def self.hourly_check def self.hourly_check
analyse_sessions analyse_sessions
complete_sessions complete_sessions
end end
def self.minutely_check
upcoming_sessions_reminder
end
def self.analyse_sessions def self.analyse_sessions
MusicSession.joins(lesson_session: :lesson_booking).where('session_removed_at IS NOT NULL').where('analysed = false').each do |music_session| MusicSession.joins(lesson_session: :lesson_booking).where('lesson_sessions.status = ?', LessonSession::STATUS_APPROVED).where("session_removed_at IS NOT NULL OR NOW() > scheduled_start + (INTERVAL '1 minutes' * duration)").where('analysed = false').each do |music_session|
lession_session = music_session.lesson_session lession_session = music_session.lesson_session
lession_session.analyse lession_session.analyse
end end
end end
def self.complete_sessions def self.complete_sessions
MusicSession.joins(lesson_session: [:lesson_booking, :lesson_payment_charge]).where('session_removed_at IS NOT NULL').where('analysed = true').where('lesson_sessions.post_processed = false').where('billing_should_retry = true').each do |music_session| # this will find any paid session (recurring monthly paid, recurring single paid, single paid)
MusicSession.joins(lesson_session: [:lesson_booking, :lesson_payment_charge]).where('lesson_sessions.status = ?', LessonSession::STATUS_COMPLETED).where("session_removed_at IS NOT NULL OR NOW() > scheduled_start + (INTERVAL '1 minutes' * duration)").where('analysed = true').where('lesson_sessions.post_processed = false').where('billing_should_retry = true').each do |music_session|
lession_session = music_session.lesson_session lession_session = music_session.lesson_session
lession_session.session_completed lession_session.session_completed
end end
# test drives don't have a lesson_payment_charge, so we don't join against them
MusicSession.joins(lesson_session: [:lesson_booking]).where('lesson_sessions.status = ?', LessonSession::STATUS_COMPLETED).where('lesson_sessions.lesson_type = ?', LESSON_TYPE_TEST_DRIVE).where("session_removed_at IS NOT NULL OR NOW() > scheduled_start + (INTERVAL '1 minutes' * duration)").where('analysed = true').where('lesson_sessions.post_processed = false').each do |music_session|
lession_session = music_session.lesson_session
lession_session.session_completed
end
end
def self.upcoming_sessions_reminder
now = Time.now
half_hour_from_now = 30.minutes.from_now
if Time.zone
now = Time.zone.local_to_utc(now)
half_hour_from_now = Time.zone.local_to_utc(half_hour_from_now)
end
MusicSession.joins(lesson_session: [:lesson_booking]).where('lesson_sessions.status = ?', LessonSession::STATUS_APPROVED).where('sent_starting_notice = false').where('(scheduled_start > ? and scheduled_start < ?)', now, half_hour_from_now).each do |music_session|
lession_session = music_session.lesson_session
lession_session.send_starting_notice
end
end end
def analyse def analyse
@ -122,12 +156,14 @@ module JamRuby
analysis = LessonSessionAnalyser.analyse(self) analysis = LessonSessionAnalyser.analyse(self)
self.analysis = analysis_to_json(analysis) self.analysis = LessonSession.analysis_to_json(analysis)
self.success = analysis[:bill] self.success = analysis[:bill]
self.analysed_at = Time.now self.analysed_at = Time.now
self.analysed = true self.analysed = true
if lesson_booking.requires_teacher_distribution?(self) self.status = STATUS_COMPLETED
if success && lesson_booking.requires_teacher_distribution?(self)
self.teacher_distribution = TeacherDistribution.create_for_lesson(self) self.teacher_distribution = TeacherDistribution.create_for_lesson(self)
end end
@ -137,12 +173,19 @@ module JamRuby
end end
end end
def billed
if lesson_booking.is_test_drive?
false
else
lesson_payment_charge.billed
end
end
def amount_charged def amount_charged
lesson_payment_charge.amount_in_cents / 100.0 lesson_payment_charge.amount_in_cents / 100.0
end end
def analysis_to_json(analysis) def self.analysis_to_json(analysis, preserve_object = false)
json = {} json = {}
analysis.each do |k, v| analysis.each do |k, v|
@ -160,7 +203,19 @@ module JamRuby
json[k] = v json[k] = v
end end
end end
json.to_json if preserve_object
json
else
json.to_json
end
end
def send_starting_notice
UserMailer.lesson_starting_soon_student(self).deliver!
UserMailer.lesson_starting_soon_teacher(self).deliver!
self.sent_starting_notice = true
self.save(validate: false)
end end
def session_completed def session_completed
@ -186,10 +241,14 @@ module JamRuby
end end
def bill_lesson def bill_lesson
if school_on_school?
success = true
else
lesson_payment_charge.charge
success = lesson_payment_charge.billed
end
lesson_payment_charge.charge if success
if lesson_payment_charge.billed
self.sent_notices = true self.sent_notices = true
self.sent_notices_at = Time.now self.sent_notices_at = Time.now
self.post_processed = true self.post_processed = true
@ -202,13 +261,13 @@ module JamRuby
def test_drive_completed def test_drive_completed
distribution = teacher_distribution distribution = teacher_distribution
if distribution # not all lessons/payment charges have a distribution
distribution.ready = true
distribution.save(validate: false)
end
if !sent_notices if !sent_notices
if success if success
if distribution # not all lessons/payment charges have a distribution
distribution.ready = true
distribution.save(validate: false)
end
student.test_drive_succeeded(self) student.test_drive_succeeded(self)
else else
student.test_drive_failed(self) student.test_drive_failed(self)
@ -242,9 +301,12 @@ module JamRuby
end end
else else
if lesson_booking.is_monthly_payment? if lesson_booking.is_monthly_payment?
# bad session; just poke user
if !sent_notices if !sent_notices
UserMailer.monthly_recurring_no_bill(self).deliver if !school_on_school?
# bad session; just poke user
UserMailer.monthly_recurring_no_bill(self).deliver
end
self.sent_notices = true self.sent_notices = true
self.sent_notices_at = Time.now self.sent_notices_at = Time.now
self.post_processed = true self.post_processed = true
@ -254,8 +316,11 @@ module JamRuby
else else
if !sent_notices if !sent_notices
# bad session; just poke user if !school_on_school?
UserMailer.student_weekly_recurring_no_bill(student, self).deliver # bad session; just poke user
UserMailer.student_lesson_normal_no_bill(self).deliver
end
self.sent_notices = true self.sent_notices = true
self.sent_notices_at = Time.now self.sent_notices_at = Time.now
self.post_processed = true self.post_processed = true
@ -272,8 +337,11 @@ module JamRuby
bill_lesson bill_lesson
else else
if !sent_notices if !sent_notices
UserMailer.student_lesson_normal_no_bill(self).deliver if !school_on_school?
UserMailer.teacher_lesson_no_bill(self).deliver UserMailer.student_lesson_normal_no_bill(self).deliver
UserMailer.teacher_lesson_normal_no_bill(self).deliver
end
self.sent_notices = true self.sent_notices = true
self.sent_notices_at = Time.now self.sent_notices_at = Time.now
self.post_processed = true self.post_processed = true
@ -314,10 +382,6 @@ module JamRuby
status == STATUS_COMPLETED status == STATUS_COMPLETED
end end
def is_missed?
status == STATUS_MISSED
end
def is_approved? def is_approved?
status == STATUS_APPROVED status == STATUS_APPROVED
end end
@ -330,6 +394,14 @@ module JamRuby
status == STATUS_COUNTERED status == STATUS_COUNTERED
end end
def analysis_json
@parsed_analysis || analysis ? JSON.parse(analysis) : nil
end
def school_on_school?
teacher.teacher.school && student.school && (teacher.teacher.school.id == student.school.id)
end
def validate_creating def validate_creating
if !is_requested? && !is_approved? if !is_requested? && !is_approved?
self.errors.add(:status, "is not valid for a new lesson session.") self.errors.add(:status, "is not valid for a new lesson session.")
@ -378,6 +450,7 @@ module JamRuby
lesson_session.teacher = booking.teacher lesson_session.teacher = booking.teacher
lesson_session.status = booking.status lesson_session.status = booking.status
lesson_session.slot = booking.default_slot lesson_session.slot = booking.default_slot
lesson_session.assigned_student = booking.student
if booking.is_test_drive? if booking.is_test_drive?
lesson_session.lesson_package_purchase = booking.student.most_recent_test_drive_purchase lesson_session.lesson_package_purchase = booking.student.most_recent_test_drive_purchase
end end
@ -393,21 +466,31 @@ module JamRuby
music_session.creator music_session.creator
end end
def student_id
music_session.creator.id
end
def self.index(user, params = {}) def self.index(user, params = {})
limit = params[:per_page] limit = params[:per_page]
limit ||= 100 limit ||= 100
limit = limit.to_i limit = limit.to_i
query = LessonSession.joins(:music_session).joins(music_session: :creator) query = LessonSession.unscoped.joins([:music_session, :lesson_booking]).joins(music_session: :creator)
query = query.includes([:teacher, :music_session]) #query = query.includes([:teacher, :music_session])
query = query.includes([:music_session])
query = query.order('music_sessions.scheduled_start DESC') query = query.order('music_sessions.scheduled_start DESC')
if params[:as_teacher] if params[:as_teacher].present?
query = query.where('lesson_sessions.teacher_id = ?', user.id) if params[:as_teacher]
query = query.where('lesson_sessions.teacher_id = ?', user.id)
else
query = query.where('music_sessions.user_id = ?', user.id)
end
else else
query = query.where('music_sessions.user_id = ?', user.id) query = query.where('(lesson_sessions.teacher_id = ? or music_sessions.user_id = ?)', user.id, user.id)
end end
query = query.where('lesson_bookings.card_presumed_ok = true OR (music_sessions.user_id = ?)', 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
@ -451,6 +534,17 @@ module JamRuby
time.nil? ? nil : attempt time.nil? ? nil : attempt
end end
def school_owner_id
school = teacher.teacher.school
if school
school.owner.id
end
end
def access?(user)
user.id == music_session.user_id || user.id == teacher.id || user.id == school_owner_id
end
# teacher accepts the lesson # teacher accepts the lesson
def accept(params) def accept(params)
response = self response = self
@ -480,8 +574,8 @@ module JamRuby
end end
UserMailer.student_lesson_accepted(self, message, slot).deliver UserMailer.student_lesson_accepted(self, message, slot).deliver
UserMailer.teacher_lesson_accepted(self, message, slot).deliver UserMailer.teacher_lesson_accepted(self, message, slot).deliver
chat_message = message ? "Lesson Approved: '#{message}'" : "Lesson Approved" message = '' if message.nil?
msg = ChatMessage.create(teacher, nil, chat_message, ChatMessage::CHANNEL_LESSON, nil, student, lesson_booking) msg = ChatMessage.create(teacher, nil, message, ChatMessage::CHANNEL_LESSON, nil, student, self, "Lesson Approved")
Notification.send_jamclass_invitation_teacher(music_session, teacher) Notification.send_jamclass_invitation_teacher(music_session, teacher)
Notification.send_student_jamclass_invitation(music_session, student) Notification.send_student_jamclass_invitation(music_session, student)
Notification.send_lesson_message('accept', self, true) Notification.send_lesson_message('accept', self, true)
@ -501,8 +595,8 @@ module JamRuby
response = lesson_booking response = lesson_booking
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
end end
chat_message = message ? "All Lesson Times Updated: '#{message}'" : "All Lesson Times Updated" message = '' if message.nil?
msg = ChatMessage.create(slot.proposer, nil, chat_message, ChatMessage::CHANNEL_LESSON, nil, slot.recipient, lesson_booking) msg = ChatMessage.create(slot.proposer, nil, message, ChatMessage::CHANNEL_LESSON, nil, slot.recipient, self, "All Lesson Times Updated")
Notification.send_lesson_message('accept', self, true) # TODO: this isn't quite an 'accept' Notification.send_lesson_message('accept', self, true) # TODO: this isn't quite an 'accept'
UserMailer.student_lesson_update_all(self, message, slot).deliver UserMailer.student_lesson_update_all(self, message, slot).deliver
UserMailer.teacher_lesson_update_all(self, message, slot).deliver UserMailer.teacher_lesson_update_all(self, message, slot).deliver
@ -515,8 +609,8 @@ module JamRuby
puts("unable to accept slot #{slot.id} for lesson #{self.id} because it's in the past") puts("unable to accept slot #{slot.id} for lesson #{self.id} because it's in the past")
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
end end
chat_message = message ? "Lesson Updated Time Approved: '#{message}'" : "Lesson Updated Time Approved" message = '' if message.nil?
msg = ChatMessage.create(slot.proposer, nil, chat_message, ChatMessage::CHANNEL_LESSON, nil, slot.recipient, lesson_booking) msg = ChatMessage.create(slot.proposer, nil, message, ChatMessage::CHANNEL_LESSON, nil, slot.recipient, self, "Lesson Updated Time Approved")
UserMailer.student_lesson_accepted(self, message, slot).deliver UserMailer.student_lesson_accepted(self, message, slot).deliver
UserMailer.teacher_lesson_accepted(self, message, slot).deliver UserMailer.teacher_lesson_accepted(self, message, slot).deliver
end end
@ -551,25 +645,42 @@ module JamRuby
self.counter_slot = slot self.counter_slot = slot
end end
if self.save if self.save
if update_all if update_all && !lesson_booking.counter(self, proposer, slot)
if !lesson_booking.counter(self, proposer, slot) response = lesson_booking
response = lesson_booking raise ActiveRecord::Rollback
raise ActiveRecord::Rollback
end
end end
else else
response = self response = self
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
end end
message = '' if message.nil?
msg = ChatMessage.create(slot.proposer, music_session, message, ChatMessage::CHANNEL_LESSON, nil, slot.recipient, lesson_booking) msg = ChatMessage.create(slot.proposer, music_session, message, ChatMessage::CHANNEL_LESSON, nil, slot.recipient, self, "New Time Proposed")
Notification.send_lesson_message('counter', self, slot.is_teacher_created?) Notification.send_lesson_message('counter', self, slot.is_teacher_created?)
end end
response response
end end
def cancel_lesson(canceler, message)
canceled_by_student = canceler == student
self.status = STATUS_CANCELED
self.cancel_message = message
self.canceler = canceler
self.canceling = true
if canceled_by_student
self.student_canceled = true
self.student_canceled_at = Time.now
self.student_canceled_reason = message
self.student_short_canceled = 24.hours.from_now > scheduled_start
else
self.teacher_canceled = true
self.teacher_canceled_at = Time.now
self.teacher_canceled_reason = message
self.teacher_short_canceled = 24.hours.from_now > scheduled_start
end
end
# teacher accepts the lesson # teacher accepts the lesson
def cancel(params) def cancel(params)
@ -577,39 +688,39 @@ module JamRuby
LessonSession.transaction do LessonSession.transaction do
canceler = params[:canceler] canceler = params[:canceler]
other = canceler == teacher ? student : teacher canceled_by_student = canceler == student
other = canceled_by_student ? teacher : student
message = params[:message] message = params[:message]
message = '' if message.nil?
if params[:update_all].present? if lesson_booking.recurring
update_all = params[:update_all] update_all = params[:update_all]
else else
update_all = !lesson_booking.recurring update_all = true
end end
if lesson_booking.is_test_drive?
student.test_drive_declined(self)
end
self.status = STATUS_CANCELED if update_all
self.cancel_message = message response = lesson_booking.cancel(canceler, other, message)
self.canceler = canceler if response.errors.any?
self.canceling = true raise ActiveRecord::Rollback
if self.save
if update_all
if !lesson_booking.cancel(canceler, other, message)
response = lesson_booking
raise ActiveRecord::Rollback
end
else
msg = ChatMessage.create(canceler, nil, message, ChatMessage::CHANNEL_LESSON, nil, other, lesson_booking)
Notification.send_lesson_message('canceled', self, false)
Notification.send_lesson_message('canceled', self, true)
UserMailer.student_lesson_canceled(self, message).deliver
UserMailer.teacher_lesson_canceled(self, message).deliver
end end
else else
response = self cancel_lesson(canceler, message)
raise ActiveRecord::Rollback if !save
end response = self
raise ActiveRecord::Rollback
end
msg = ChatMessage.create(canceler, nil, message, ChatMessage::CHANNEL_LESSON, nil, other, self, "Lesson Canceled")
Notification.send_lesson_message('canceled', self, false)
Notification.send_lesson_message('canceled', self, true)
UserMailer.student_lesson_canceled(self, message).deliver
UserMailer.teacher_lesson_canceled(self, message).deliver
end
end end
response response
@ -619,6 +730,19 @@ module JamRuby
lesson_booking.lesson_package_type.description(lesson_booking) lesson_booking.lesson_package_type.description(lesson_booking)
end end
def timed_description
if is_test_drive?
"TestDrive session with #{self.lesson_booking.student.name} on #{self.scheduled_start.to_date.strftime('%B %d, %Y')}"
else
if self.lesson_booking.is_monthly_payment?
"Monthly Lesson with #{self.lesson_booking.student.name} on #{self.scheduled_start.to_date.strftime('%B %d, %Y')}"
else
"Lesson with #{self.lesson_booking.student.name} on #{self.scheduled_start.to_date.strftime('%B %d, %Y')}"
end
end
end
def stripe_description(lesson_booking) def stripe_description(lesson_booking)
description(lesson_booking) description(lesson_booking)
end end
@ -638,5 +762,9 @@ module JamRuby
def admin_url def admin_url
APP_CONFIG.admin_root_url + "/admin/lesson_sessions/" + id APP_CONFIG.admin_root_url + "/admin/lesson_sessions/" + id
end end
def chat_url
APP_CONFIG.external_root_url + "/client#/jamclass/chat-dialog/d1=lesson_" + id
end
end end
end end

View File

@ -1,7 +1,7 @@
module JamRuby module JamRuby
class LessonSessionAnalyser class LessonSessionAnalyser
SUCCESS = 'success' SUCCESS = 'success'
SESSION_ONGOING = 'session_ongoing' SESSION_ONGOING = 'session_ongoing'
THRESHOLD_MET = 'threshold_met' THRESHOLD_MET = 'threshold_met'
WAITED_CORRECTLY = 'waited_correctly' WAITED_CORRECTLY = 'waited_correctly'
@ -16,6 +16,7 @@ module JamRuby
STUDENT_NOT_THERE_WHEN_JOINED = 'student_not_there_when_joined' STUDENT_NOT_THERE_WHEN_JOINED = 'student_not_there_when_joined'
JOINED_LATE = 'did_not_join_on_time' JOINED_LATE = 'did_not_join_on_time'
NO_SHOW = 'no_show' NO_SHOW = 'no_show'
NEITHER_SHOW = 'neither_show'
# what are the potential results? # what are the potential results?
@ -42,7 +43,7 @@ module JamRuby
# reason: 'both_fault' # reason: 'both_fault'
def self.analyse(lesson_session) def self.analyse(lesson_session, force = false)
reason = nil reason = nil
teacher = nil teacher = nil
student = nil student = nil
@ -62,26 +63,25 @@ module JamRuby
teacher_ranges = merge_overlapping_ranges(all_teacher_ranges) teacher_ranges = merge_overlapping_ranges(all_teacher_ranges)
intersecting = intersecting_ranges(student_ranges, teacher_ranges) intersecting = intersecting_ranges(student_ranges, teacher_ranges)
student_analysis = analyse_intersection(lesson_session, student_ranges) student_analysis = analyse_intersection(lesson_session, student_ranges)
teacher_analysis = analyse_intersection(lesson_session, teacher_ranges) teacher_analysis = analyse_intersection(lesson_session, teacher_ranges)
together_analysis = analyse_intersection(lesson_session, intersecting) together_analysis = analyse_intersection(lesson_session, intersecting)
# spec: https://jamkazam.atlassian.net/wiki/display/PS/Product+Specification+-+JamClass#ProductSpecification-JamClass-TeacherReceives&RespondstoLessonBookingRequest # spec: https://jamkazam.atlassian.net/wiki/display/PS/Product+Specification+-+JamClass#ProductSpecification-JamClass-TeacherReceives&RespondstoLessonBookingRequest
if music_session.session_removed_at.nil? if !force && !((music_session.scheduled_start + (lesson_session.duration * 60)) < Time.now)
reason = SESSION_ONGOING reason = SESSION_ONGOING
bill = false bill = false
else else
if lesson_session.is_canceled? && lesson_session.canceled_by_teacher? && lesson_session.canceled_late? #if lesson_session.is_canceled? && lesson_session.canceled_by_teacher? && lesson_session.canceled_late?
# If the lesson was cancelled less than 24 hours before the start time by the teacher, then we do not bill the student. # # If the lesson was cancelled less than 24 hours before the start time by the teacher, then we do not bill the student.
teacher = LATE_CANCELLATION # teacher = LATE_CANCELLATION
bill = false # bill = false
elsif lesson_session.is_canceled? && lesson_session.canceled_by_student? && lesson_session.canceled_late? #elsif lesson_session.is_canceled? && lesson_session.canceled_by_student? && lesson_session.canceled_late?
# If the lesson was cancelled less than 24 hours before the start time by the student (if that is even possible, I cant remember now), then we do bill the student. # # If the lesson was cancelled less than 24 hours before the start time by the student (if that is even possible, I cant remember now), then we do bill the student.
student = LATE_CANCELLATION # student = LATE_CANCELLATION
bill = true # bill = true
elsif together_analysis[:session_time] / 60 > APP_CONFIG.lesson_together_threshold_minutes if together_analysis[:session_time] / 60 > APP_CONFIG.lesson_together_threshold_minutes
bill = true bill = true
reason = SUCCESS reason = SUCCESS
elsif teacher_analysis[:joined_on_time] && teacher_analysis[:waited_correctly] elsif teacher_analysis[:joined_on_time] && teacher_analysis[:waited_correctly]
@ -98,8 +98,17 @@ module JamRuby
student = JOINED_LATE student = JOINED_LATE
bill = true bill = true
end end
else
if teacher_analysis[:no_show]
teacher = NO_SHOW
elsif !teacher_analysis[:joined_on_time]
teacher = JOINED_LATE
elsif !teacher_analysis[:waited_correctly]
teacher = MINIMUM_TIME_NOT_MET
end
end end
end end
if reason.nil? if reason.nil?
@ -107,6 +116,8 @@ module JamRuby
reason = STUDENT_FAULT reason = STUDENT_FAULT
elsif teacher elsif teacher
reason = TEACHER_FAULT reason = TEACHER_FAULT
else
reason = NEITHER_SHOW
end end
end end
@ -150,7 +161,8 @@ module JamRuby
def self.analyse_intersection(lesson_session, ranges) def self.analyse_intersection(lesson_session, ranges)
start = lesson_session.scheduled_start # be sure to call .to_time on any ActiveRecord time, because we get a ton of deprecation warninsg about Time#succ if you use ActiveSupport:: TimeZone
start = lesson_session.scheduled_start.to_time
planned_duration_seconds = lesson_session.duration * 60 planned_duration_seconds = lesson_session.duration * 60
end_time = start + planned_duration_seconds end_time = start + planned_duration_seconds
@ -286,7 +298,7 @@ module JamRuby
end end
def self.ranges_overlap?(a, b) def self.ranges_overlap?(a, b)
a.include?(b.begin) || b.include?(a.begin) a.cover?(b.begin) || b.cover?(a.begin)
end end
def self.merge_ranges(a, b) def self.merge_ranges(a, b)

View File

@ -6,7 +6,19 @@ 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?
times = 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]
session = data[:session]
true_start = start_day
if session
# if there is already a session for the month, that is the real star
true_start = session.scheduled_start.to_date
end
# filter out anything before the start day
times.select! { |time| time.to_date >= true_start }
result = nil result = nil
if times.length == 0 if times.length == 0

View File

@ -22,6 +22,7 @@ module JamRuby
CREATE_TYPE_IMMEDIATE = 'immediately' CREATE_TYPE_IMMEDIATE = 'immediately'
CREATE_TYPE_QUICK_START = 'quick-start' CREATE_TYPE_QUICK_START = 'quick-start'
CREATE_TYPE_LESSON = 'lesson' CREATE_TYPE_LESSON = 'lesson'
CREATE_TYPE_QUICK_PUBLIC = 'quick-public'
attr_accessor :legal_terms, :language_description, :access_description, :scheduling_info_changed attr_accessor :legal_terms, :language_description, :access_description, :scheduling_info_changed
@ -323,11 +324,29 @@ module JamRuby
# let session be restarted for up to 2 hours after finishing # let session be restarted for up to 2 hours after finishing
session_finished = "(music_sessions.session_removed_at > NOW() - '2 hour'::INTERVAL)" session_finished = "(music_sessions.session_removed_at > NOW() - '2 hour'::INTERVAL)"
query = MusicSession.where("music_sessions.canceled = FALSE") query = MusicSession.joins(
%Q{
LEFT OUTER JOIN
invitations
ON
music_sessions.id = invitations.music_session_id AND invitations.receiver_id = '#{user.id}'
}
)
query = query.where("music_sessions.canceled = FALSE")
query = query.where('music_sessions.fan_access = TRUE or music_sessions.musician_access = TRUE') if only_public query = query.where('music_sessions.fan_access = TRUE or music_sessions.musician_access = TRUE') if only_public
query = query.where("music_sessions.user_id = '#{user.id}'") #query = query.where("music_sessions.user_id = '#{user.id}' OR invitations.id IS NOT NULL")
query = query.where("music_sessions.id in (
select distinct(rs.music_session_id)
from rsvp_slots rs
where rs.id in (
select rrrs.rsvp_slot_id
from rsvp_requests rr
inner join rsvp_requests_rsvp_slots rrrs on rr.id = rrrs.rsvp_request_id
where rr.user_id = '#{user.id}'AND rrrs.chosen = true
)
) OR invitations.id IS NOT NULL OR music_sessions.user_id = '#{user.id}'")
query = query.where("music_sessions.scheduled_start IS NULL OR #{session_not_started} OR #{session_finished} OR #{session_started_not_finished}") query = query.where("music_sessions.scheduled_start IS NULL OR #{session_not_started} OR #{session_finished} OR #{session_started_not_finished}")
query = query.where("music_sessions.create_type IS NULL OR music_sessions.create_type != '#{CREATE_TYPE_QUICK_START}'") query = query.where("music_sessions.create_type IS NULL OR (music_sessions.create_type != '#{CREATE_TYPE_QUICK_START}' AND music_sessions.create_type != '#{CREATE_TYPE_QUICK_PUBLIC}')")
query = query.order("music_sessions.scheduled_start ASC") query = query.order("music_sessions.scheduled_start ASC")
query query
@ -339,7 +358,7 @@ module JamRuby
filter_approved = only_approved ? 'AND rrrs.chosen = true' : '' filter_approved = only_approved ? 'AND rrrs.chosen = true' : ''
MusicSession.where(%Q{music_sessions.canceled = FALSE AND MusicSession.where(%Q{music_sessions.canceled = FALSE AND
(music_sessions.create_type is NULL OR music_sessions.create_type != '#{CREATE_TYPE_QUICK_START}') AND (music_sessions.create_type is NULL OR (music_sessions.create_type != '#{CREATE_TYPE_QUICK_START}' AND music_sessions.create_type != '#{CREATE_TYPE_QUICK_PUBLIC}')) AND
(music_sessions.scheduled_start is NULL OR music_sessions.scheduled_start > NOW() - '4 hour'::INTERVAL) AND (music_sessions.scheduled_start is NULL OR music_sessions.scheduled_start > NOW() - '4 hour'::INTERVAL) AND
music_sessions.id in ( music_sessions.id in (
select distinct(rs.music_session_id) select distinct(rs.music_session_id)
@ -760,7 +779,7 @@ module JamRuby
query = query.offset(offset) query = query.offset(offset)
query = query.limit(limit) query = query.limit(limit)
query = query.where("music_sessions.create_type IS NULL OR (music_sessions.create_type != ? AND music_sessions.create_type != ?)", MusicSession::CREATE_TYPE_QUICK_START, MusicSession::CREATE_TYPE_IMMEDIATE) query = query.where("music_sessions.create_type IS NULL OR (music_sessions.create_type != ? AND music_sessions.create_type != ? AND music_sessions.create_type != ?)", MusicSession::CREATE_TYPE_QUICK_START, MusicSession::CREATE_TYPE_IMMEDIATE, MusicSession::CREATE_TYPE_QUICK_PUBLIC)
query = query.where("music_sessions.genre_id = ?", genre) unless genre.blank? query = query.where("music_sessions.genre_id = ?", genre) unless genre.blank?
query = query.where('music_sessions.language = ?', lang) unless lang.blank? query = query.where('music_sessions.language = ?', lang) unless lang.blank?
query = query.where("(description_tsv @@ to_tsquery('jamenglish', ?))", ActiveRecord::Base.connection.quote(keyword) + ':*') unless keyword.blank? query = query.where("(description_tsv @@ to_tsquery('jamenglish', ?))", ActiveRecord::Base.connection.quote(keyword) + ':*') unless keyword.blank?
@ -875,7 +894,7 @@ SQL
result result
end end
def scheduled_start_date def scheduled_start_date
if self.scheduled_start_time.blank? if self.scheduled_start_time.blank?
"" ""
else else
@ -936,20 +955,16 @@ SQL
end end
duration duration
end end
# should create a timestamp like:
# def pretty_scheduled_start(with_timezone = true, shorter = false)
# with_timezone = TRUE
# Tuesday, April 29, 8:00-9:00 PM TIMEZONE (where TIMEZONE is the TIMEZONE defined in the MusicSession when it was created)
#
# with_timezone = FALSE
# Thursday, July 10 - 10:00pm
# this should be in a helper
def pretty_scheduled_start(with_timezone = true)
if scheduled_start && scheduled_duration if scheduled_start && scheduled_duration
start_time = scheduled_start start_time = scheduled_start
timezone_display = 'UTC' timezone_display = 'UTC'
utc_offset_display = '00:00'
tz_identifier, tz_display = MusicSession.split_timezone(timezone) tz_identifier, tz_display = MusicSession.split_timezone(timezone)
short_tz = 'GMT'
begin begin
tz = TZInfo::Timezone.get(tz_identifier) tz = TZInfo::Timezone.get(tz_identifier)
rescue Exception => e rescue Exception => e
@ -960,6 +975,15 @@ SQL
begin begin
start_time = tz.utc_to_local(scheduled_start.utc) start_time = tz.utc_to_local(scheduled_start.utc)
timezone_display = tz_display timezone_display = tz_display
utc_offset_hours = tz.current_period.utc_total_offset / (60*60)
hour = sprintf '%02d', utc_offset_hours.abs
minutes = sprintf '%02d', ((tz.current_period.utc_total_offset.abs % 3600) / 3600) * 60
utc_offset_display = "#{utc_offset_hours < 0 ? '-' : ' '}#{hour}:#{minutes}"
short_tz = start_time.strftime("%Z")
if short_tz == 'UTC'
short_tz = 'GMT'
end
rescue Exception => e rescue Exception => e
@@log.error("unable to convert #{scheduled_start} to #{tz}, e=#{e}") @@log.error("unable to convert #{scheduled_start} to #{tz}, e=#{e}")
puts "unable to convert #{e}" puts "unable to convert #{e}"
@ -969,7 +993,11 @@ SQL
duration = safe_scheduled_duration duration = safe_scheduled_duration
end_time = start_time + duration end_time = start_time + duration
if with_timezone if with_timezone
"#{start_time.strftime("%A, %B %e")}, #{start_time.strftime("%l:%M").strip}-#{end_time.strftime("%l:%M %p").strip} #{timezone_display}" if shorter
"#{start_time.strftime("%a, %b %e %Y")}, #{start_time.strftime("%l:%M").strip}-#{end_time.strftime("%l:%M %p").strip} (#{short_tz}#{utc_offset_display})"
else
"#{start_time.strftime("%A, %B %e")}, #{start_time.strftime("%l:%M").strip}-#{end_time.strftime("%l:%M %p").strip} #{timezone_display}"
end
else else
"#{start_time.strftime("%A, %B %e")} - #{start_time.strftime("%l:%M%P").strip}" "#{start_time.strftime("%A, %B %e")} - #{start_time.strftime("%l:%M%P").strip}"
end end

View File

@ -26,7 +26,9 @@ module JamRuby
end end
def range def range
Range.new(created_at, session_removed_at || Time.now) # to_time to not use ActiveSupport::TimeWithZone
session_removed_at_time = session_removed_at.to_time if session_removed_at
Range.new(created_at.to_time, session_removed_at_time || Time.now.to_time)
end end
def music_session def music_session
@ -38,9 +40,9 @@ module JamRuby
end end
def self.save(music_session_id, user_id, client_id, tracks) def self.save(music_session_id, user_id, client_id, tracks)
return true if 0 < self.where(:music_session_id => music_session_id, return true if 0 < self.where(:music_session_id => music_session_id,
:user_id => user_id, :user_id => user_id,
:client_id => client_id).count :client_id => client_id).where('session_removed_at is NULL').count
session_user_history = MusicSessionUserHistory.new session_user_history = MusicSessionUserHistory.new
session_user_history.music_session_id = music_session_id session_user_history.music_session_id = music_session_id
session_user_history.user_id = user_id session_user_history.user_id = user_id
@ -58,10 +60,12 @@ module JamRuby
(end_time - self.created_at) / 60.0 (end_time - self.created_at) / 60.0
end end
def self.join_music_session(user_id, session_id) def self.join_music_session(user_id, session_id, client_id)
hist = self hist = self
.where(:user_id => user_id) .where(:user_id => user_id)
.where(:music_session_id => session_id) .where(:music_session_id => session_id)
.where(:client_id => client_id)
.where('session_removed_at IS NULL')
.limit(1) .limit(1)
.first .first
hist.start_history if hist hist.start_history if hist

View File

@ -63,7 +63,7 @@ module JamRuby
end end
end end
self.class.format_msg(self.description, {:user => source_user, target: target_user, :band => band, :session => session, purpose: purpose, student_directed: student_directed}) self.class.format_msg(self.description, {:user => source_user, target: target_user, :band => band, :session => session, purpose: purpose, student_directed: student_directed, msg: message})
end end
# TODO: MAKE ALL METHODS BELOW ASYNC SO THE CLIENT DOESN'T BLOCK ON NOTIFICATION LOGIC # TODO: MAKE ALL METHODS BELOW ASYNC SO THE CLIENT DOESN'T BLOCK ON NOTIFICATION LOGIC
@ -135,6 +135,7 @@ module JamRuby
session = options[:session] session = options[:session]
purpose = options[:purpose] purpose = options[:purpose]
student_directed = options[:student_directed] student_directed = options[:student_directed]
msg = options[:msg]
name, band_name = "" name, band_name = ""
unless user.nil? unless user.nil?
@ -271,6 +272,8 @@ module JamRuby
end end
elsif purpose == 'reschedule' elsif purpose == 'reschedule'
'A lesson reschedule has been requested' 'A lesson reschedule has been requested'
elsif purpose == 'chat'
notification_msg = "Lesson Message: #{msg}"
end end
return notification_msg return notification_msg
@ -404,7 +407,7 @@ module JamRuby
end end
end end
def send_lesson_message(purpose, lesson_session, student_directed) def send_lesson_message(purpose, lesson_session, student_directed, msg = nil)
notification = Notification.new notification = Notification.new
notification.description = NotificationTypes::LESSON_MESSAGE notification.description = NotificationTypes::LESSON_MESSAGE
@ -422,9 +425,11 @@ module JamRuby
notification.session_id = lesson_session.music_session.id notification.session_id = lesson_session.music_session.id
notification.lesson_session_id = lesson_session.id notification.lesson_session_id = lesson_session.id
notification_msg = format_msg(NotificationTypes::LESSON_MESSAGE, {purpose: purpose}) notification_msg = format_msg(NotificationTypes::LESSON_MESSAGE, {purpose: purpose, msg: msg})
#notification.message = notification_msg if purpose == 'chat'
notification.message = msg
end
notification.save notification.save
@ -756,7 +761,7 @@ module JamRuby
end end
begin begin
UserMailer.teacher_scheduled_jamclass_invitation(music_session.lesson_session.teacher, notification_msg, music_session).deliver #UserMailer.teacher_scheduled_jamclass_invitation(music_session.lesson_session.teacher, notification_msg, music_session).deliver
rescue => e rescue => e
@@log.error("Unable to send SCHEDULED_JAMCLASS_INVITATION email to user #{music_session.lesson_session.teacher.email} #{e}") @@log.error("Unable to send SCHEDULED_JAMCLASS_INVITATION email to user #{music_session.lesson_session.teacher.email} #{e}")
end end
@ -796,7 +801,7 @@ module JamRuby
end end
begin begin
UserMailer.student_scheduled_jamclass_invitation(student, notification_msg, music_session).deliver #UserMailer.student_scheduled_jamclass_invitation(student, notification_msg, music_session).deliver
rescue => e rescue => e
@@log.error("Unable to send SCHEDULED_JAMCLASS_INVITATION email to user #{student.email} #{e}") @@log.error("Unable to send SCHEDULED_JAMCLASS_INVITATION email to user #{student.email} #{e}")
end end
@ -1094,7 +1099,7 @@ module JamRuby
# start in less than 24 hours, and haven't been # start in less than 24 hours, and haven't been
# notified for a particular interval yet: # notified for a particular interval yet:
def send_session_reminders def send_session_reminders
MusicSession.where("scheduled_start > NOW() AND scheduled_start <= (NOW()+INTERVAL '1 DAYS')").each do |candidate_session| MusicSession.where("scheduled_start > NOW() AND scheduled_start <= (NOW()+INTERVAL '1 DAYS') AND lesson_session_id IS NULL").each do |candidate_session|
tm = candidate_session.scheduled_start tm = candidate_session.scheduled_start
if (tm>(12.hours.from_now) && !notified?(candidate_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_DAY)) if (tm>(12.hours.from_now) && !notified?(candidate_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_DAY))
# Send 24 hour reminders: # Send 24 hour reminders:

View File

@ -5,6 +5,7 @@ module JamRuby
belongs_to :sale belongs_to :sale
belongs_to :recurly_transaction_web_hook belongs_to :recurly_transaction_web_hook
belongs_to :charge
def self.index(user, params = {}) def self.index(user, params = {})
@ -14,7 +15,7 @@ module JamRuby
limit = limit.to_i limit = limit.to_i
query = PaymentHistory.limit(limit) query = PaymentHistory.limit(limit)
.includes(sale: [:sale_line_items], recurly_transaction_web_hook:[]) .includes(sale: [:sale_line_items], recurly_transaction_web_hook:[], charge:[])
.where(user_id: user.id) .where(user_id: user.id)
.where("transaction_type = 'sale' OR transaction_type = 'refund' OR transaction_type = 'void'") .where("transaction_type = 'sale' OR transaction_type = 'refund' OR transaction_type = 'void'")
.order('created_at DESC') .order('created_at DESC')

View File

@ -208,8 +208,8 @@ module JamRuby
free && non_free free && non_free
end end
def self.purchase_test_drive(current_user, booking = nil) def self.purchase_test_drive(current_user, lesson_package_type, booking = nil)
self.purchase_lesson(current_user, booking, LessonPackageType.test_drive) self.purchase_lesson(current_user, booking, lesson_package_type)
end end
def self.purchase_normal(current_user, booking) def self.purchase_normal(current_user, booking)
@ -273,6 +273,7 @@ module JamRuby
purchase = LessonPackagePurchase.create(current_user, lesson_booking, lesson_package_type) if purchase.nil? purchase = LessonPackagePurchase.create(current_user, lesson_booking, lesson_package_type) 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

@ -43,14 +43,17 @@ module JamRuby
JamTrack.find_by_id(product_id) JamTrack.find_by_id(product_id)
elsif product_type == GIFTCARD elsif product_type == GIFTCARD
GiftCardType.find_by_id(product_id) GiftCardType.find_by_id(product_id)
elsif product_type == LESSON
lesson_package_purchase
else else
raise 'unsupported product type' raise 'unsupported product type'
end end
end end
def product_info def product_info
item = product item = product
{ name: product.name } if item { name: product.name, product_type: product_type } if item
end end
def state def state

View File

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

View File

@ -44,10 +44,25 @@ module JamRuby
SignupHint.where("created_at < :week", {:week => 1.week.ago}).delete_all SignupHint.where("created_at < :week", {:week => 1.week.ago}).delete_all
end end
def self.most_recent_redirect(user, default) def self.most_recent_redirect(user, default, queryParams=nil)
puts "jquery params"
hint = SignupHint.where(user_id: user.id).order('created_at desc').first hint = SignupHint.where(user_id: user.id).order('created_at desc').first
if hint if hint
hint.redirect_location redirect = hint.redirect_location
puts "redirect #{redirect}"
uri = URI.parse(redirect)
bits = uri.query ? URI.decode_www_form(uri.query) : []
if queryParams
queryParams.each do |k, v|
bits << [k, v]
end
end
puts "bits #{bits}"
uri.query = URI.encode_www_form(bits)
puts "oh yeah #{uri.to_s}"
return uri.to_s
else else
default default
end end

View File

@ -55,7 +55,7 @@ module JamRuby
query = User.joins(:teacher) query = User.joins(:teacher)
# only show teachers with background check set and ready for session set to true # only show teachers with ready for session set to true
query = query.where('teachers.ready_for_session_at IS NOT NULL') query = query.where('teachers.ready_for_session_at IS NOT NULL')
instruments = params[:instruments] instruments = params[:instruments]
@ -145,7 +145,11 @@ module JamRuby
def self.save_teacher(user, params) def self.save_teacher(user, params)
teacher = build_teacher(user, params) teacher = build_teacher(user, params)
teacher.save if teacher.save
# flag the user as a teacher
teacher.user.is_a_teacher = true
teacher.user.save(validate: false)
end
teacher teacher
end end
@ -190,8 +194,10 @@ module JamRuby
teacher.price_per_month_120_cents = params[:price_per_month_120_cents] if params.key?(:price_per_month_120_cents) teacher.price_per_month_120_cents = params[:price_per_month_120_cents] if params.key?(:price_per_month_120_cents)
teacher.teaches_test_drive = params[:teaches_test_drive] if params.key?(:teaches_test_drive) teacher.teaches_test_drive = params[:teaches_test_drive] if params.key?(:teaches_test_drive)
teacher.test_drives_per_week = params[:test_drives_per_week] if params.key?(:test_drives_per_week) teacher.test_drives_per_week = params[:test_drives_per_week] if params.key?(:test_drives_per_week)
teacher.test_drives_per_week ||= 10 # default to 10 in absence of others
teacher.school_id = params[:school_id] if params.key?(:school_id) teacher.school_id = params[:school_id] if params.key?(:school_id)
# Many-to-many relations: # Many-to-many relations:
if params.key?(:genres) if params.key?(:genres)
genres = params[:genres] genres = params[:genres]
@ -317,7 +323,7 @@ module JamRuby
end end
def has_stripe_billing? def has_stripe_billing?
false user.has_stripe_connect?
end end
def has_instruments_or_subject? def has_instruments_or_subject?

View File

@ -5,10 +5,43 @@ module JamRuby
belongs_to :teacher_payment, class_name: "JamRuby::TeacherPayment" belongs_to :teacher_payment, class_name: "JamRuby::TeacherPayment"
belongs_to :lesson_session, class_name: "JamRuby::LessonSession" belongs_to :lesson_session, class_name: "JamRuby::LessonSession"
belongs_to :lesson_package_purchase, class_name: "JamRuby::LessonPackagePurchase" belongs_to :lesson_package_purchase, class_name: "JamRuby::LessonPackagePurchase"
belongs_to :school, class_name: "JamRuby::School"
validates :teacher, presence: true validates :teacher, presence: true
validates :amount_in_cents, presence: true validates :amount_in_cents, presence: true
def self.index(current_user, params)
limit = params[:per_page]
limit ||= 100
limit = limit.to_i
query = TeacherDistribution.where(teacher_id: current_user.id).where('school_id IS NULL').order('created_at desc')
current_page = params[:page].nil? ? 1 : params[:page].to_i
next_page = current_page + 1
# will_paginate gem
query = query.paginate(:page => current_page, :per_page => limit)
if query.length == 0 # no more results
{query: query, next_page: nil}
elsif query.length < limit # no more results
{query: query, next_page: nil}
else
{query: query, next_page: next_page}
end
end
def not_collectable
if is_test_drive?
false
elsif is_normal?
!lesson_session.billing_should_retry
else
! lesson_package_purchase.billing_should_retry
end
end
def self.create_for_lesson(lesson_session) def self.create_for_lesson(lesson_session)
distribution = create(lesson_session) distribution = create(lesson_session)
distribution.lesson_session = lesson_session distribution.lesson_session = lesson_session
@ -26,13 +59,35 @@ 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 distribution.amount_in_cents = target.lesson_booking.distribution_price_in_cents(target)
distribution.school = target.lesson_booking.school
distribution distribution
end end
def amount def amount
amount_in_cents / 100.0 amount_in_cents / 100.0
end end
def real_distribution_in_cents
amount_in_cents - calculate_teacher_fee
end
def real_distribution
(real_distribution_in_cents / 100.0)
end
def real_distribution_display
'$%.2f' % real_distribution
end
def calculate_teacher_fee
if is_test_drive?
0
else
(amount_in_cents * (teacher.teacher.jamkazam_rate + 0.03)).round
end
end
def student def student
if lesson_session if lesson_session
lesson_session.student lesson_session.student
@ -59,17 +114,9 @@ module JamRuby
def description def description
if lesson_session if lesson_session
if lesson_session.lesson_booking.is_test_drive? lesson_session.timed_description
"Test Drive session with #{lesson_session.lesson_booking.student.name} on #{lesson_session.scheduled_start.to_date}"
elsif lesson_session.lesson_booking.is_normal?
if lesson_session.lesson_booking.is_weekly_payment? || lesson_session.lesson_booking.is_monthly_payment?
raise "Should not be here"
else
"A session with #{lesson_session.lesson_booking.student.name} on #{lesson_session.scheduled_start.to_date}"
end
end
else else
"Monthly session for the month of #{lesson_package_purchase.month_name} with #{lesson_package_purchase.lesson_booking.student.name}" lesson_package_purchase.description
end end
end end
end end

View File

@ -4,12 +4,22 @@ module JamRuby
belongs_to :teacher, class_name: "JamRuby::User", foreign_key: :teacher_id belongs_to :teacher, class_name: "JamRuby::User", foreign_key: :teacher_id
belongs_to :teacher_payment_charge, class_name: "JamRuby::TeacherPaymentCharge", foreign_key: :charge_id belongs_to :teacher_payment_charge, class_name: "JamRuby::TeacherPaymentCharge", foreign_key: :charge_id
has_one :teacher_distribution, class_name: "JamRuby::TeacherDistribution" has_one :teacher_distribution, class_name: "JamRuby::TeacherDistribution"
belongs_to :school, class_name: "JamRuby::School"
def self.hourly_check def self.hourly_check
teacher_payments teacher_payments
end end
# pay the school if the payment owns the school; otherwise default to the teacher
def payable_teacher
if school
school.owner
else
teacher
end
end
def teacher_distributions def teacher_distributions
[teacher_distribution] [teacher_distribution]
end end
@ -51,15 +61,10 @@ module JamRuby
24 24
end end
def calculate_teacher_fee def real_distribution_in_cents
if teacher_distribution.is_test_drive? amount_in_cents - fee_in_cents
0
else
(amount_in_cents * 0.28).round
end
end end
# will find, for a given teacher, an outstading unsuccessful payment or make a new one. # will find, for a given teacher, an outstading unsuccessful payment or make a new one.
# it will then associate a charge with it, and then execute the charge. # it will then associate a charge with it, and then execute the charge.
def self.charge(teacher) def self.charge(teacher)
@ -79,12 +84,13 @@ module JamRuby
payment.teacher_distribution = teacher_distribution payment.teacher_distribution = teacher_distribution
end end
payment.school = payment.teacher_distribution.school
payment.amount_in_cents = payment.teacher_distribution.amount_in_cents payment.amount_in_cents = payment.teacher_distribution.amount_in_cents
payment.fee_in_cents = payment.calculate_teacher_fee payment.fee_in_cents = payment.teacher_distribution.calculate_teacher_fee
if payment.teacher_payment_charge.nil? if payment.teacher_payment_charge.nil?
charge = TeacherPaymentCharge.new charge = TeacherPaymentCharge.new
charge.user = payment.payable_teacher
charge.amount_in_cents = payment.amount_in_cents charge.amount_in_cents = payment.amount_in_cents
charge.fee_in_cents = payment.fee_in_cents charge.fee_in_cents = payment.fee_in_cents
charge.teacher_payment = payment charge.teacher_payment = payment

View File

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

View File

@ -51,7 +51,7 @@ module JamRuby
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization" has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"
has_many :reviews, :class_name => "JamRuby::Review" has_many :reviews, :class_name => "JamRuby::Review"
has_one :review_summary, :class_name => "JamRuby::ReviewSummary" has_one :review_summary, :class_name => "JamRuby::ReviewSummary"
# calendars (for scheduling NOT in music_session) # calendars (for scheduling NOT in music_session)
has_many :calendars, :class_name => "JamRuby::Calendar" has_many :calendars, :class_name => "JamRuby::Calendar"
@ -71,7 +71,7 @@ module JamRuby
has_many :band_musicians, :class_name => "JamRuby::BandMusician" has_many :band_musicians, :class_name => "JamRuby::BandMusician"
has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band" has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band"
has_one :teacher, :class_name => "JamRuby::Teacher" has_one :teacher, :class_name => "JamRuby::Teacher"
# genres # genres
has_many :genre_players, as: :player, class_name: "JamRuby::GenrePlayer", dependent: :destroy has_many :genre_players, as: :player, class_name: "JamRuby::GenrePlayer", dependent: :destroy
has_many :genres, through: :genre_players, class_name: "JamRuby::Genre" has_many :genres, through: :genre_players, class_name: "JamRuby::Genre"
@ -101,7 +101,7 @@ module JamRuby
has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy
# text messages # text messages
has_many :text_messages, :class_name => "JamRuby:TextMessage", :foreign_key => "target_user_id" has_many :text_messages, :class_name => "JamRuby::TextMessage", :foreign_key => "target_user_id"
# notifications # notifications
has_many :notifications, :class_name => "JamRuby::Notification", :foreign_key => "target_user_id" has_many :notifications, :class_name => "JamRuby::Notification", :foreign_key => "target_user_id"
@ -176,6 +176,7 @@ module JamRuby
has_many :teacher_lesson_bookings, :class_name => "JamRuby::LessonBooking", :foreign_key => "teacher_id", inverse_of: :teacher has_many :teacher_lesson_bookings, :class_name => "JamRuby::LessonBooking", :foreign_key => "teacher_id", inverse_of: :teacher
has_many :teacher_distributions, :class_name => "JamRuby::TeacherDistribution", :foreign_key => "teacher_id", inverse_of: :teacher has_many :teacher_distributions, :class_name => "JamRuby::TeacherDistribution", :foreign_key => "teacher_id", inverse_of: :teacher
has_many :teacher_payments, :class_name => "JamRuby::TeacherPayment", :foreign_key => "teacher_id", inverse_of: :teacher has_many :teacher_payments, :class_name => "JamRuby::TeacherPayment", :foreign_key => "teacher_id", inverse_of: :teacher
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
# Shopping carts # Shopping carts
has_many :shopping_carts, :class_name => "JamRuby::ShoppingCart" has_many :shopping_carts, :class_name => "JamRuby::ShoppingCart"
@ -209,6 +210,8 @@ module JamRuby
before_save :create_remember_token, :if => :should_validate_password? before_save :create_remember_token, :if => :should_validate_password?
before_save :stringify_avatar_info, :if => :updating_avatar before_save :stringify_avatar_info, :if => :updating_avatar
after_save :after_save
validates :first_name, length: {maximum: 50}, no_profanity: true validates :first_name, length: {maximum: 50}, no_profanity: true
validates :last_name, length: {maximum: 50}, no_profanity: true validates :last_name, length: {maximum: 50}, no_profanity: true
validates :biography, length: {maximum: 4000}, no_profanity: true validates :biography, length: {maximum: 4000}, no_profanity: true
@ -255,11 +258,22 @@ module JamRuby
scope :musicians_geocoded, musicians.geocoded_users scope :musicians_geocoded, musicians.geocoded_users
scope :email_opt_in, where(:subscribe_email => true) scope :email_opt_in, where(:subscribe_email => true)
def after_save
if school_interest && !school_interest_was
AdminMailer.partner({body: "#{email} signed up via the https://www.jamkazam.com/landing/jamclass/schools page.\n\nFull list is here: https://www.jamkazam.com/admin/admin/school_interests", subject: "#{email} is interested in schools"}).deliver
if owned_school.nil?
school = School.new
school.user = self
school.save!
end
end
end
def update_teacher_pct def update_teacher_pct
if teacher if teacher
teacher.update_profile_pct teacher.update_profile_pct
end end
end end
def user_progression_fields def user_progression_fields
@user_progression_fields ||= Set.new ["first_downloaded_client_at", "first_ran_client_at", "first_music_session_at", "first_real_music_session_at", "first_good_music_session_at", "first_certified_gear_at", "first_invited_at", "first_friended_at", "first_recording_at", "first_social_promoted_at", "first_played_jamtrack_at"] @user_progression_fields ||= Set.new ["first_downloaded_client_at", "first_ran_client_at", "first_music_session_at", "first_real_music_session_at", "first_good_music_session_at", "first_certified_gear_at", "first_invited_at", "first_friended_at", "first_recording_at", "first_social_promoted_at", "first_played_jamtrack_at"]
end end
@ -1090,6 +1104,7 @@ module JamRuby
teacher = options[:teacher] teacher = options[:teacher]
school_invitation_code = options[:school_invitation_code] school_invitation_code = options[:school_invitation_code]
school_id = options[:school_id] school_id = options[:school_id]
school_interest = options[:school_interest]
user = User.new user = User.new
user.validate_instruments = true user.validate_instruments = true
@ -1115,6 +1130,7 @@ module JamRuby
user.has_redeemable_jamtrack = true user.has_redeemable_jamtrack = true
user.is_a_student = !!student user.is_a_student = !!student
user.is_a_teacher = !!teacher user.is_a_teacher = !!teacher
user.school_interest = !!school_interest
if user.is_a_student || user.is_a_teacher if user.is_a_student || user.is_a_teacher
musician = true musician = true
end end
@ -1125,7 +1141,7 @@ module JamRuby
user.school_id = school_id user.school_id = school_id
elsif user.is_a_teacher elsif user.is_a_teacher
school = School.find_by_id(school_id) school = School.find_by_id(school_id)
school_name = school ? school.name : 'a music school' school_name = school ? school.name : 'a music school'
user.teacher = Teacher.build_teacher(user, validate_introduction: true, biography: "Teaches for #{school_name}", school_id: school_id) user.teacher = Teacher.build_teacher(user, validate_introduction: true, biography: "Teaches for #{school_name}", school_id: school_id)
end end
else else
@ -1318,16 +1334,13 @@ module JamRuby
end if affiliate_referral_id.present? end if affiliate_referral_id.present?
if user.is_a_student if user.is_a_student
UserMailer.student_welcome_message(user).deliver UserMailer.student_welcome_message(user).deliver
end elsif user.is_a_teacher
if user.is_a_teacher
UserMailer.teacher_welcome_message(user).deliver UserMailer.teacher_welcome_message(user).deliver
end elsif user.school_interest
UserMailer.school_owner_welcome_message(user).deliver
if !user.is_a_teacher && !user.is_a_student else
UserMailer.welcome_message(user).deliver UserMailer.welcome_message(user).deliver
end end
@ -1891,7 +1904,7 @@ module JamRuby
end end
def can_buy_test_drive? def can_buy_test_drive?
lesson_purchases.where(lesson_package_type_id: LessonPackageType.test_drive.id).where('created_at > ?', APP_CONFIG.test_drive_wait_period_year.years.ago).count == 0 lesson_purchases.where('lesson_package_type_id in (?)', LessonPackageType.test_drive_package_ids).where('created_at > ?', APP_CONFIG.test_drive_wait_period_year.years.ago).count == 0
end end
def has_test_drives? def has_test_drives?
@ -1906,6 +1919,15 @@ module JamRuby
!requested_test_drive(teacher).nil? !requested_test_drive(teacher).nil?
end end
def stripe_auth
user_authorizations.where(provider: "stripe_connect").first
end
def has_stripe_connect?
auth = stripe_auth
auth && (!auth.token_expiration || auth.token_expiration > Time.now)
end
def fetch_stripe_customer def fetch_stripe_customer
Stripe::Customer.retrieve(stripe_customer_id) Stripe::Customer.retrieve(stripe_customer_id)
end end
@ -1931,9 +1953,11 @@ module JamRuby
customer customer
end end
def card_approved(token, zip)
def card_approved(token, zip, booking_id)
approved_booking = nil approved_booking = nil
found_uncollectables = nil
User.transaction do User.transaction do
self.stripe_token = token if token self.stripe_token = token if token
self.stripe_zip_code = zip if zip self.stripe_zip_code = zip if zip
@ -1941,14 +1965,22 @@ module JamRuby
self.stripe_customer_id = customer.id self.stripe_customer_id = customer.id
self.stored_credit_card = true self.stored_credit_card = true
if self.save if self.save
# we can also 'unlock' any booked sessions that still need to be done so if booking_id
LessonBooking.unprocessed(self).each do |booking| approved_booking = LessonBooking.find_by_id(booking_id)
booking.card_approved if approved_booking
approved_booking = booking approved_booking.card_approved
end
end
if uncollectables.count > 0
found_uncollectables = uncollectables
uncollectables.update_all(billing_should_retry: true)
else
found_uncollectables = nil
end end
end end
end end
approved_booking [approved_booking, found_uncollectables]
end end
def update_name(name) def update_name(name)
@ -1977,6 +2009,8 @@ module JamRuby
normal = nil normal = nil
intent = nil intent = nil
purchase = nil purchase = nil
lesson_package_type = nil
uncollectables = nil
User.transaction do User.transaction do
if params[:name].present? if params[:name].present?
@ -1985,20 +2019,32 @@ module JamRuby
end end
end end
booking = card_approved(params[:token], params[:zip]) booking, uncollectables = card_approved(params[:token], params[:zip], params[:booking_id])
if params[:test_drive] if params[:test_drive]
self.reload self.reload
result = Sale.purchase_test_drive(self, booking) if booking
lesson_package_type = booking.resolved_test_drive_package
end
if lesson_package_type.nil?
lesson_package_type = LessonPackageType.test_drive_4
end
result = Sale.purchase_test_drive(self, lesson_package_type, booking)
test_drive = result[:sale] test_drive = result[:sale]
purchase = result[:purchase] purchase = result[:purchase]
if booking && !purchase.errors.any?
# the booking would not have a lesson_package_purchase associated yet, so let's associate it
booking.lesson_sessions.update_all(lesson_package_purchase_id: purchase.id)
end
elsif params[:normal] elsif params[:normal]
self.reload self.reload
end end
intent = TeacherIntent.recent_test_drive(self)
end end
{lesson: booking, test_drive: test_drive, intent:intent, purchase: purchase} {lesson: booking, test_drive: test_drive, purchase: purchase, lesson_package_type: lesson_package_type, uncollectables: uncollectables}
end end
def requested_test_drive(teacher = nil) def requested_test_drive(teacher = nil)
@ -2018,7 +2064,16 @@ module JamRuby
end end
def most_recent_test_drive_purchase def most_recent_test_drive_purchase
lesson_purchases.where(lesson_package_type_id: LessonPackageType.test_drive.id).order('created_at desc').first lesson_purchases.where('lesson_package_type_id in (?)', [LessonPackageType.test_drive_package_ids]).order('created_at desc').first
end
def total_test_drives
purchase = most_recent_test_drive_purchase
if purchase
purchase.test_drive_count
else
0
end
end end
def test_drive_succeeded(lesson_session) def test_drive_succeeded(lesson_session)
@ -2031,16 +2086,26 @@ module JamRuby
end end
end end
def test_drive_declined(lesson_session)
# because we decrement test_drive credits as soon as you book, we need to bring it back now
self.remaining_test_drives = self.remaining_test_drives + 1
self.save(validate: false)
end
def test_drive_failed(lesson_session) def test_drive_failed(lesson_session)
# because we decrement test_drive credits as soon as you book, we need to bring it back now # because we decrement test_drive credits as soon as you book, we need to bring it back now
self.remaining_test_drives = self.remaining_test_drives + 1 self.remaining_test_drives = self.remaining_test_drives + 1
self.save(validate: false) self.save(validate: false)
UserMailer.test_drive_no_bill(self, lesson_session).deliver UserMailer.student_test_drive_no_bill(lesson_session).deliver
end end
def used_test_drives def used_test_drives
4 - remaining_test_drives total_test_drives - remaining_test_drives
end
def uncollectables(limit = 10)
LessonPaymentCharge.where(user_id:self.id).order(:created_at).where('billing_attempts > 0').where(billed: false).limit(limit)
end end
def has_rated_teacher(teacher) def has_rated_teacher(teacher)

View File

@ -0,0 +1,16 @@
module JamRuby
class MinutelyJob
extend Resque::Plugins::JamLonelyJob
@queue = :scheduled_minutely_job
@@log = Logging.logger[MinutelyJob]
def self.perform
@@log.debug("waking up")
LessonSession.minutely_check
@@log.debug("done")
end
end
end

View File

@ -980,7 +980,7 @@ FactoryGirl.define do
price 30.00 price 30.00
factory :test_drive_purchase do factory :test_drive_purchase do
lesson_package_type { JamRuby::LessonPackageType.test_drive } lesson_package_type { JamRuby::LessonPackageType.test_drive_4 }
association :lesson_booking, factory: :lesson_booking association :lesson_booking, factory: :lesson_booking
price 49.99 price 49.99
end end
@ -1010,6 +1010,13 @@ FactoryGirl.define do
factory :teacher_payment_charge, parent: :charge, class: 'JamRuby::TeacherPaymentCharge' do factory :teacher_payment_charge, parent: :charge, class: 'JamRuby::TeacherPaymentCharge' do
type 'JamRuby::TeacherPaymentCharge' type 'JamRuby::TeacherPaymentCharge'
association :user, factory: :user
end
factory :lesson_payment_charge, parent: :charge, class: 'JamRuby::LessonPaymentCharge' do
type 'JamRuby::LessonPaymentCharge'
association :user, factory: :user
end end

View File

@ -38,11 +38,11 @@ describe "Monthly Recurring Lesson Flow" do
########## Need validate their credit card ########## Need validate their credit card
token = create_stripe_token token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', normal: true}) result = user.payment_update({token: token, zip: '78759', normal: true})
booking = result[:lesson] booking.card_presumed_ok.should be_true
lesson = booking.lesson_sessions[0]
booking.errors.any?.should be_false booking.errors.any?.should be_false
lesson.errors.any?.should be_false lesson.errors.any?.should be_false
booking.card_presumed_ok.should be_true booking = result[:lesson]
lesson = booking.lesson_sessions[0]
booking.sent_notices.should be_true booking.sent_notices.should be_true
lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
lesson.amount_charged.should be 0.0 lesson.amount_charged.should be 0.0
@ -116,7 +116,7 @@ describe "Monthly Recurring Lesson Flow" do
# puts del.inspect # puts del.inspect
end end
# get acceptance emails, as well as 'your stuff is accepted' # get acceptance emails, as well as 'your stuff is accepted'
UserMailer.deliveries.length.should eql 6 UserMailer.deliveries.length.should eql 2
lesson_session.errors.any?.should be_false lesson_session.errors.any?.should be_false
lesson_session.reload lesson_session.reload
lesson_session.slot.should eql student_counter lesson_session.slot.should eql student_counter
@ -126,9 +126,10 @@ describe "Monthly Recurring Lesson Flow" do
lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
booking.status.should eql LessonBooking::STATUS_APPROVED booking.status.should eql LessonBooking::STATUS_APPROVED
UserMailer.deliveries.length.should eql 6 UserMailer.deliveries.length.should eql 2
chat = ChatMessage.unscoped.order(:created_at).last chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql "Lesson Approved: 'Yeah I got this'" chat.message.should eql 'Yeah I got this'
chat.purpose.should eql 'Lesson Approved'
chat.channel.should eql ChatMessage::CHANNEL_LESSON chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.user.should eql teacher_user chat.user.should eql teacher_user
chat.target_user.should eql user chat.target_user.should eql user
@ -185,7 +186,7 @@ describe "Monthly Recurring Lesson Flow" do
lesson_purchase.sale_line_item.should eql line_item lesson_purchase.sale_line_item.should eql line_item
TeacherPayment.count.should eql 0 TeacherPayment.count.should eql 0
TeacherPayment.daily_check TeacherPayment.hourly_check
teacher_distribution.reload teacher_distribution.reload
teacher_distribution.distributed.should be_true teacher_distribution.distributed.should be_true
TeacherPayment.count.should eql 1 TeacherPayment.count.should eql 1

View File

@ -12,9 +12,12 @@ describe "Normal Lesson Flow" do
let(:lesson_booking_slot_recurring2) { FactoryGirl.build(:lesson_booking_slot_recurring) } let(:lesson_booking_slot_recurring2) { FactoryGirl.build(:lesson_booking_slot_recurring) }
let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] } let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] }
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] } let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
let(:school) {FactoryGirl.create(:school)}
describe "stripe mocked" do describe "stripe mocked" do
before { StripeMock.start before {
StripeMock.clear_errors
StripeMock.start
teacher.stripe_account_id = stripe_account1_id teacher.stripe_account_id = stripe_account1_id
teacher.save! teacher.save!
} }
@ -35,7 +38,7 @@ describe "Normal Lesson Flow" do
########## Need validate their credit card ########## Need validate their credit card
token = create_stripe_token token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', normal: true}) result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
booking = result[:lesson] booking = result[:lesson]
lesson = booking.lesson_sessions[0] lesson = booking.lesson_sessions[0]
booking.errors.any?.should be_false booking.errors.any?.should be_false
@ -71,9 +74,10 @@ describe "Normal Lesson Flow" do
lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
booking.status.should eql LessonBooking::STATUS_APPROVED booking.status.should eql LessonBooking::STATUS_APPROVED
UserMailer.deliveries.length.should eql 4 UserMailer.deliveries.length.should eql 2
chat = ChatMessage.unscoped.order(:created_at).last chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql "Lesson Approved: 'Yeah I got this'" chat.message.should eql 'Yeah I got this'
chat.purpose.should eql 'Lesson Approved'
chat.channel.should eql ChatMessage::CHANNEL_LESSON chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.user.should eql teacher_user chat.user.should eql teacher_user
chat.target_user.should eql user chat.target_user.should eql user
@ -91,6 +95,7 @@ describe "Normal Lesson Flow" do
lesson_session.music_session.session_removed_at = end_time lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save! lesson_session.music_session.save!
Timecop.travel(end_time + 1)
UserMailer.deliveries.clear UserMailer.deliveries.clear
# background code comes around and analyses the session # background code comes around and analyses the session
@ -177,12 +182,12 @@ describe "Normal Lesson Flow" do
lesson_session.reload lesson_session.reload
payment = lesson_session.lesson_payment_charge payment = lesson_session.lesson_payment_charge
payment.amount_in_cents.should eql 3248 payment.amount_in_cents.should eql 3248
payment.fee_in_cents.should eql 0 payment.fee_in_cents.should eql 0
lesson_session.billing_attempts.should eql 4
lesson_session.post_processed.should be_true lesson_session.post_processed.should be_true
LessonPaymentCharge.count.should eql 2 LessonPaymentCharge.count.should eql 1
lesson_session.reload lesson_session.reload
lesson_session.analysed.should be_true lesson_session.analysed.should be_true
@ -266,7 +271,7 @@ describe "Normal Lesson Flow" do
########## Need validate their credit card ########## Need validate their credit card
token = create_stripe_token token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', normal: true}) result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
booking = result[:lesson] booking = result[:lesson]
lesson = booking.lesson_sessions[0] lesson = booking.lesson_sessions[0]
booking.errors.any?.should be_false booking.errors.any?.should be_false
@ -351,9 +356,10 @@ describe "Normal Lesson Flow" do
lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
booking.status.should eql LessonBooking::STATUS_APPROVED booking.status.should eql LessonBooking::STATUS_APPROVED
UserMailer.deliveries.length.should eql 4 UserMailer.deliveries.length.should eql 2
chat = ChatMessage.unscoped.order(:created_at).last chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql "Lesson Approved: 'Yeah I got this'" chat.message.should eql 'Yeah I got this'
chat.purpose.should eql 'Lesson Approved'
chat.channel.should eql ChatMessage::CHANNEL_LESSON chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.user.should eql teacher_user chat.user.should eql teacher_user
chat.target_user.should eql user chat.target_user.should eql user
@ -371,6 +377,7 @@ describe "Normal Lesson Flow" do
lesson_session.music_session.session_removed_at = end_time lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save! lesson_session.music_session.save!
Timecop.travel(end_time + 1)
UserMailer.deliveries.clear UserMailer.deliveries.clear
# background code comes around and analyses the session # background code comes around and analyses the session
@ -411,4 +418,167 @@ describe "Normal Lesson Flow" do
user.remaining_test_drives.should eql 0 user.remaining_test_drives.should eql 0
UserMailer.deliveries.length.should eql 2 # one for student, one for teacher UserMailer.deliveries.length.should eql 2 # one for student, one for teacher
end end
it "works (school on school)" do
# get user and teacher into same school
user.school = school
user.save!
teacher.school = school
teacher.save!
# user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking.book_normal(user, teacher_user, valid_single_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60)
booking.errors.any?.should be_false
booking.school.should be_true
booking.card_presumed_ok.should be_false
booking.user.should eql user
user.unprocessed_normal_lesson.should eql []
booking.sent_notices.should be_false
booking.booked_price.should eql 30.00
booking.is_requested?.should be_true
booking.sent_notices.should be_true
lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
LessonPaymentCharge.count.should eql 0
user.reload
user.stripe_customer_id.should_not be nil
user.remaining_test_drives.should eql 0
user.lesson_purchases.length.should eql 0
customer = Stripe::Customer.retrieve(user.stripe_customer_id)
customer.email.should eql user.email
booking.lesson_sessions.length.should eql 1
lesson_session = booking.lesson_sessions[0]
lesson_session.status.should eql LessonBooking::STATUS_REQUESTED
booking.status.should eql LessonBooking::STATUS_REQUESTED
######### Teacher counters with new slot
teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 14)
UserMailer.deliveries.clear
lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
booking.reload
booking.errors.any?.should be false
lesson_session.lesson_booking.errors.any?.should be false
lesson_session.lesson_booking_slots.length.should eql 1
lesson_session.lesson_booking_slots[0].proposer.should eql teacher_user
teacher_counter = lesson_session.lesson_booking_slots.order(:created_at).last
teacher_counter.should eql teacher_countered_slot
teacher_counter.proposer.should eql teacher_user
booking.lesson_booking_slots.length.should eql 3
UserMailer.deliveries.length.should eql 1
chat = ChatMessage.unscoped.order(:created_at).last
chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.message.should eql 'Does this work?'
chat.user.should eql teacher_user
chat.target_user.should eql user
notification = Notification.unscoped.order(:created_at).last
notification.session_id.should eql lesson_session.music_session.id
notification.student_directed.should eql true
notification.purpose.should eql 'counter'
notification.description.should eql NotificationTypes::LESSON_MESSAGE
######### Student counters with new slot
student_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 16)
UserMailer.deliveries.clear
lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
lesson_session.errors.any?.should be false
lesson_session.lesson_booking.errors.any?.should be false
lesson_session.lesson_booking_slots.length.should eql 2
student_counter = booking.lesson_booking_slots.order(:created_at).last
student_counter.proposer.should eql user
booking.reload
booking.lesson_booking_slots.length.should eql 4
UserMailer.deliveries.length.should eql 1
chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql 'Does this work better?'
chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.user.should eql user
chat.target_user.should eql teacher_user
notification = Notification.unscoped.order(:created_at).last
notification.session_id.should eql lesson_session.music_session.id
notification.student_directed.should eql false
notification.purpose.should eql 'counter'
notification.description.should eql NotificationTypes::LESSON_MESSAGE
######## Teacher accepts slot
UserMailer.deliveries.clear
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false})
lesson_session.errors.any?.should be_false
lesson_session.reload
lesson_session.slot.should eql student_counter
lesson_session.status.should eql LessonSession::STATUS_APPROVED
booking.reload
booking.default_slot.should eql student_counter
lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
booking.status.should eql LessonBooking::STATUS_APPROVED
UserMailer.deliveries.length.should eql 2
chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql 'Yeah I got this'
chat.purpose.should eql 'Lesson Approved'
chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.user.should eql teacher_user
chat.target_user.should eql user
notification = Notification.unscoped.order(:created_at).last
notification.session_id.should eql lesson_session.music_session.id
notification.student_directed.should eql true
notification.purpose.should eql 'accept'
notification.description.should eql NotificationTypes::LESSON_MESSAGE
# teacher & student get into session
start = lesson_session.scheduled_start
end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
# artificially end the session, which is covered by other background jobs
lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save!
Timecop.travel(end_time + 1)
UserMailer.deliveries.clear
# background code comes around and analyses the session
LessonSession.hourly_check
lesson_session.reload
lesson_session.analysed.should be_true
analysis = JSON.parse(lesson_session.analysis)
analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
if lesson_session.billing_error_detail
puts "testdrive flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
end
lesson_session.billed.should be true
user.reload
user.lesson_purchases.length.should eql 1
lesson_purchase = user.lesson_purchases[0]
lesson_purchase.price.should eql 30.00
lesson_purchase.lesson_package_type.is_normal?.should eql true
lesson_purchase.price_in_cents.should eql 3000
user.sales.length.should eql 1
sale = user.sales.first
sale.stripe_charge_id.should_not be_nil
sale.recurly_tax_in_cents.should eql (100 * booking.booked_price.to_f * 0.0825).round.to_i
sale.recurly_total_in_cents.should eql ((100 * booking.booked_price.to_f * 0.0825).round + 100 * booking.booked_price.to_f).to_i
sale.recurly_subtotal_in_cents.should eql (100 * booking.booked_price).to_i
sale.recurly_currency.should eql 'USD'
sale.stripe_charge_id.should_not be_nil
line_item = sale.sale_line_items[0]
line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.single.id
line_item.lesson_package_purchase.should eql lesson_purchase
lesson_purchase.sale_line_item.should eql line_item
lesson.amount_charged.should eql (sale.recurly_total_in_cents / 100.0).to_f
lesson_session.billing_error_reason.should be_nil
lesson_session.sent_billing_notices.should be true
user.reload
user.remaining_test_drives.should eql 0
UserMailer.deliveries.length.should eql 2 # one for student, one for teacher
LessonPaymentCharge.count.should eql 0
TeacherDistribution.count.should eql 0
end
end end

View File

@ -13,6 +13,9 @@ describe "Recurring Lesson Flow" do
let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] } let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] }
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] } let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
before(:each) do
Timecop.return
end
it "works" do it "works" do
# user has no test drives, no credit card on file, but attempts to book a lesson # user has no test drives, no credit card on file, but attempts to book a lesson
@ -27,7 +30,7 @@ describe "Recurring Lesson Flow" do
########## Need validate their credit card ########## Need validate their credit card
token = create_stripe_token token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', normal: true}) result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
booking = result[:lesson] booking = result[:lesson]
lesson = booking.lesson_sessions[0] lesson = booking.lesson_sessions[0]
booking.errors.any?.should be_false booking.errors.any?.should be_false
@ -107,7 +110,7 @@ describe "Recurring Lesson Flow" do
# puts del.inspect # puts del.inspect
end end
# get acceptance emails, as well as 'your stuff is accepted' # get acceptance emails, as well as 'your stuff is accepted'
UserMailer.deliveries.length.should eql 6 UserMailer.deliveries.length.should eql 2
lesson_session.errors.any?.should be_false lesson_session.errors.any?.should be_false
lesson_session.reload lesson_session.reload
lesson_session.slot.should eql student_counter lesson_session.slot.should eql student_counter
@ -117,9 +120,10 @@ describe "Recurring Lesson Flow" do
lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
booking.status.should eql LessonBooking::STATUS_APPROVED booking.status.should eql LessonBooking::STATUS_APPROVED
UserMailer.deliveries.length.should eql 6 UserMailer.deliveries.length.should eql 2
chat = ChatMessage.unscoped.order(:created_at).last chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql "Lesson Approved: 'Yeah I got this'" chat.message.should eql "Yeah I got this"
chat.purpose.should eql 'Lesson Approved'
chat.channel.should eql ChatMessage::CHANNEL_LESSON chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.user.should eql teacher_user chat.user.should eql teacher_user
chat.target_user.should eql user chat.target_user.should eql user
@ -138,6 +142,7 @@ describe "Recurring Lesson Flow" do
lesson_session.music_session.session_removed_at = end_time lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save! lesson_session.music_session.save!
Timecop.travel(end_time + 1)
UserMailer.deliveries.clear UserMailer.deliveries.clear
# background code comes around and analyses the session # background code comes around and analyses the session

View File

@ -14,12 +14,16 @@ describe "TestDrive Lesson Flow" do
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] } let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
before { before {
Timecop.return
teacher.stripe_account_id = stripe_account1_id teacher.stripe_account_id = stripe_account1_id
teacher.save! teacher.save!
} }
it "works" do it "works" do
user.desired_package = LessonPackageType.test_drive_2
user.save!
# user has no test drives, no credit card on file, but attempts to book a lesson # user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.") booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
booking.errors.any?.should be_false booking.errors.any?.should be_false
@ -28,20 +32,24 @@ describe "TestDrive Lesson Flow" do
booking.card_presumed_ok.should be_false booking.card_presumed_ok.should be_false
booking.should eql user.unprocessed_test_drive booking.should eql user.unprocessed_test_drive
booking.sent_notices.should be_false booking.sent_notices.should be_false
user.reload user.reload
user.remaining_test_drives.should eql 0 user.remaining_test_drives.should eql 0
########## Need validate their credit card ########## Need validate their credit card
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, booking_id: booking.id})
user.reload
user.remaining_test_drives.should eql 3
booking = result[:lesson] booking = result[:lesson]
lesson = booking.lesson_sessions[0]
test_drive = result[:test_drive]
booking.errors.any?.should be_false booking.errors.any?.should be_false
lesson = booking.lesson_sessions[0]
lesson.errors.any?.should be_false lesson.errors.any?.should be_false
test_drive = result[:test_drive]
test_drive.errors.any?.should be_false test_drive.errors.any?.should be_false
user.reload
user.remaining_test_drives.should eql 1
booking.card_presumed_ok.should be_true booking.card_presumed_ok.should be_true
booking.sent_notices.should be_true booking.sent_notices.should be_true
lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
@ -49,20 +57,20 @@ describe "TestDrive Lesson Flow" do
test_drive.stripe_charge_id.should_not be_nil test_drive.stripe_charge_id.should_not be_nil
test_drive.recurly_tax_in_cents.should be 412 test_drive.recurly_tax_in_cents.should be 247
test_drive.recurly_total_in_cents.should eql 4999 + 412 test_drive.recurly_total_in_cents.should eql 2999 + 247
test_drive.recurly_subtotal_in_cents.should eql 4999 test_drive.recurly_subtotal_in_cents.should eql 2999
test_drive.recurly_currency.should eql 'USD' test_drive.recurly_currency.should eql 'USD'
line_item = test_drive.sale_line_items[0] line_item = test_drive.sale_line_items[0]
line_item.quantity.should eql 1 line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.test_drive.id line_item.product_id.should eq LessonPackageType.test_drive_2.id
user.reload user.reload
user.stripe_customer_id.should_not be nil user.stripe_customer_id.should_not be nil
user.lesson_purchases.length.should eql 1 user.lesson_purchases.length.should eql 1
user.remaining_test_drives.should eql 3 user.remaining_test_drives.should eql 1
lesson_purchase = user.lesson_purchases[0] lesson_purchase = user.lesson_purchases[0]
lesson_purchase.price.should eql 49.99 lesson_purchase.price.should eql 29.99
lesson_purchase.lesson_package_type.is_test_drive?.should eql true lesson_purchase.lesson_package_type.is_test_drive?.should eql true
customer = Stripe::Customer.retrieve(user.stripe_customer_id) customer = Stripe::Customer.retrieve(user.stripe_customer_id)
@ -134,7 +142,7 @@ describe "TestDrive Lesson Flow" do
lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
booking.status.should eql LessonBooking::STATUS_APPROVED booking.status.should eql LessonBooking::STATUS_APPROVED
UserMailer.deliveries.length.should eql 4 UserMailer.deliveries.length.should eql 2
chat = ChatMessage.unscoped.order(:created_at).last chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql 'Yeah I got this' chat.message.should eql 'Yeah I got this'
chat.channel.should eql ChatMessage::CHANNEL_LESSON chat.channel.should eql ChatMessage::CHANNEL_LESSON
@ -145,7 +153,7 @@ describe "TestDrive Lesson Flow" do
notification.student_directed.should eql true notification.student_directed.should eql true
notification.purpose.should eql 'accept' notification.purpose.should eql 'accept'
notification.description.should eql NotificationTypes::LESSON_MESSAGE notification.description.should eql NotificationTypes::LESSON_MESSAGE
notification.message.should eql "Your lesson request is confirmed!" notification.message.should be_nil
# teacher & student get into session # teacher & student get into session
@ -156,6 +164,7 @@ describe "TestDrive Lesson Flow" do
lesson_session.music_session.session_removed_at = end_time lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save! lesson_session.music_session.save!
Timecop.travel(end_time + 1)
UserMailer.deliveries.clear UserMailer.deliveries.clear
@ -173,11 +182,11 @@ describe "TestDrive Lesson Flow" do
lesson_session.billing_error_reason.should be_nil lesson_session.billing_error_reason.should be_nil
lesson_session.sent_notices.should be true lesson_session.sent_notices.should be true
purchase = lesson_session.lesson_package_purchase purchase = lesson_session.lesson_package_purchase
purchase.should_not be nil purchase.should_not be_nil
purchase.price_in_cents.should eql 4999 purchase.price_in_cents.should eql 2999
purchase.lesson_package_type.is_test_drive?.should be true purchase.lesson_package_type.is_test_drive?.should be true
user.reload user.reload
user.remaining_test_drives.should eql 3 user.remaining_test_drives.should eql 1
UserMailer.deliveries.length.should eql 2 # one for student, one for teacher UserMailer.deliveries.length.should eql 2 # one for student, one for teacher
teacher_distribution = lesson_session.teacher_distribution teacher_distribution = lesson_session.teacher_distribution
@ -194,7 +203,7 @@ describe "TestDrive Lesson Flow" do
teacher_distribution.distributed.should be_false teacher_distribution.distributed.should be_false
TeacherPayment.count.should eql 0 TeacherPayment.count.should eql 0
TeacherPayment.daily_check TeacherPayment.hourly_check
TeacherPayment.count.should eql 1 TeacherPayment.count.should eql 1
lesson_session.reload lesson_session.reload

View File

@ -783,6 +783,22 @@ describe AffiliatePartner do
end end
end end
describe "edge case" do
it "year change" do
partner.touch
last_day_of_year = Date.new(2015, 12, 31)
first_day_of_next_year = Date.new(2016, 01, 01)
AffiliatePartner.tally_up(last_day_of_year)
AffiliatePartner.tally_up(first_day_of_next_year)
quarterly_payment = AffiliateQuarterlyPayment.where(year: 2016, quarter: 0, affiliate_partner_id: partner.id).first
quarterly_payment.closed.should be_false
AffiliatePartner.tally_up(Date.new(2016, 01, 02))
quarterly_payment.reload
quarterly_payment.closed.should be_false
end
end
describe "boundary_dates_for_month" do describe "boundary_dates_for_month" do
it "invalid month" do it "invalid month" do
expect{AffiliatePartner.boundary_dates_for_month(2015, 0)}.to raise_error expect{AffiliatePartner.boundary_dates_for_month(2015, 0)}.to raise_error

View File

@ -0,0 +1,11 @@
require 'spec_helper'
describe Language do
it "english_sort" do
sorted= Language.english_sort
sorted[0].id.should eql 'EN'
sorted[1].id.should eql 'AF'
sorted[-1].id.should eql 'XH'
end
end

View File

@ -112,7 +112,7 @@ describe LessonBooking do
purchase.last_billing_attempt_at.should eql time purchase.last_billing_attempt_at.should eql time
purchase.sent_billing_notices.should eql false purchase.sent_billing_notices.should eql false
user.card_approved(create_stripe_token, '78759') user.card_approved(create_stripe_token, '78759', booking.id)
user.save! user.save!
day = day + 1 day = day + 1
@ -137,7 +137,7 @@ describe LessonBooking do
end end
it "advances to next month" do it "advances to next month" do
user.card_approved(create_stripe_token, '78759') user.card_approved(create_stripe_token, '78759', nil)
user.save! user.save!
day = Date.new(2016, 1, 20) day = Date.new(2016, 1, 20)
@ -258,7 +258,7 @@ describe LessonBooking do
purchase.billed.should be false purchase.billed.should be false
# now that it's suspended, let's unsuspend it # now that it's suspended, let's unsuspend it
user.card_approved(create_stripe_token, '78759') user.card_approved(create_stripe_token, '78759', booking.id)
user.save! user.save!
day = day + 1 day = day + 1
@ -386,38 +386,6 @@ describe LessonBooking do
end end
describe "book_free" do describe "book_free" do
it "works" do
pending "free not supported"
booking = LessonBooking.book_free(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
booking.errors.any?.should be false
booking.user.should eq user
booking.teacher.should eq teacher_user
booking.description.should eq ("Hey I've heard of you before.")
booking.payment_style.should eq LessonBooking::PAYMENT_STYLE_ELSEWHERE
booking.recurring.should eq false
booking.lesson_length.should eq 30
booking.lesson_type.should eq LessonBooking::LESSON_TYPE_FREE
booking.lesson_booking_slots.length.should eq 2
chat_message = ChatMessage.where(lesson_booking_id: booking.id).first
chat_message.should_not be_nil
chat_message.message.should eq booking.description
user.reload
user.remaining_free_lessons.should eq 0
user.remaining_test_drives.should eq 1
booking.card_presumed_ok.should eq false
booking.sent_notices.should eq false
user.card_approved(create_stripe_token, '78759')
user.save!
booking.reload
booking.sent_notices.should eq true
booking.card_presumed_ok.should eq true
end
it "allows long message to flow through chat" do it "allows long message to flow through chat" do
@ -426,7 +394,7 @@ describe LessonBooking do
booking.errors.any?.should be false booking.errors.any?.should be false
chat_message = ChatMessage.where(lesson_booking_id: booking.id).first chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first
chat_message.should_not be_nil chat_message.should_not be_nil
chat_message.message.should eq booking.description chat_message.message.should eq booking.description
end end
@ -446,16 +414,6 @@ describe LessonBooking do
ChatMessage.count.should eq 1 ChatMessage.count.should eq 1
end end
it "prevents user without stored credit card" do
pending "free not supported"
user.stored_credit_card = false
user.save!
booking = LessonBooking.book_free(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
booking.errors.any?.should be false
end
it "must have 2 lesson booking slots" do it "must have 2 lesson booking slots" do
booking = LessonBooking.book_test_drive(user, teacher_user, [], "Hey I've heard of you before.") booking = LessonBooking.book_test_drive(user, teacher_user, [], "Hey I've heard of you before.")
@ -488,7 +446,7 @@ describe LessonBooking do
booking.lesson_type.should eq LessonBooking::LESSON_TYPE_TEST_DRIVE booking.lesson_type.should eq LessonBooking::LESSON_TYPE_TEST_DRIVE
booking.lesson_booking_slots.length.should eq 2 booking.lesson_booking_slots.length.should eq 2
chat_message = ChatMessage.where(lesson_booking_id: booking.id).first chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first
chat_message.should_not be_nil chat_message.should_not be_nil
chat_message.message.should eq booking.description chat_message.message.should eq booking.description
@ -502,7 +460,7 @@ describe LessonBooking do
booking.errors.any?.should be false booking.errors.any?.should be false
chat_message = ChatMessage.where(lesson_booking_id: booking.id).first chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first
chat_message.should_not be_nil chat_message.should_not be_nil
chat_message.message.should eq booking.description chat_message.message.should eq booking.description
end end
@ -520,7 +478,7 @@ describe LessonBooking do
booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.") booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
booking.errors.any?.should be true booking.errors.any?.should be true
booking.errors[:user].should eq ["has a requested TestDrive with this teacher"] booking.errors[:user].should eq ["have a requested TestDrive with this teacher"]
ChatMessage.count.should eq 1 ChatMessage.count.should eq 1
end end
@ -549,7 +507,7 @@ describe LessonBooking do
booking.lesson_type.should eq LessonBooking::LESSON_TYPE_PAID booking.lesson_type.should eq LessonBooking::LESSON_TYPE_PAID
booking.lesson_booking_slots.length.should eq 2 booking.lesson_booking_slots.length.should eq 2
chat_message = ChatMessage.where(lesson_booking_id: booking.id).first chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first
chat_message.should_not be_nil chat_message.should_not be_nil
chat_message.message.should eq booking.description chat_message.message.should eq booking.description
@ -570,7 +528,7 @@ describe LessonBooking do
booking.lesson_type.should eq LessonBooking::LESSON_TYPE_PAID booking.lesson_type.should eq LessonBooking::LESSON_TYPE_PAID
booking.lesson_booking_slots.length.should eq 2 booking.lesson_booking_slots.length.should eq 2
chat_message = ChatMessage.where(lesson_booking_id: booking.id).first chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first
chat_message.should_not be_nil chat_message.should_not be_nil
chat_message.message.should eq booking.description chat_message.message.should eq booking.description
@ -579,12 +537,12 @@ describe LessonBooking do
user.remaining_test_drives.should eq 1 user.remaining_test_drives.should eq 1
end end
it "allows long message to flow through chat" do it "allows long message to flow through chat" do
booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, Faker::Lorem.characters(10000), true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, Faker::Lorem.characters(10000), true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60)
booking.errors.any?.should be false booking.errors.any?.should be false
chat_message = ChatMessage.where(lesson_booking_id: booking.id).first chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first
chat_message.should_not be_nil chat_message.should_not be_nil
chat_message.message.should eq booking.description chat_message.message.should eq booking.description
end end
@ -709,6 +667,7 @@ describe LessonBooking do
Timecop.freeze(7.days.ago) Timecop.freeze(7.days.ago)
lesson_session.cancel({canceler: teacher_user, message: 'meh', slot: booking.default_slot.id, update_all: false}) lesson_session.cancel({canceler: teacher_user, message: 'meh', slot: booking.default_slot.id, update_all: false})
lesson_session.errors.any?.should be_false lesson_session.errors.any?.should be_false
lesson_session.reload
lesson_session.status.should eql LessonSession::STATUS_CANCELED lesson_session.status.should eql LessonSession::STATUS_CANCELED
lesson_session.reload lesson_session.reload
booking.reload booking.reload
@ -732,8 +691,8 @@ describe LessonBooking do
Timecop.freeze(7.days.ago) Timecop.freeze(7.days.ago)
lesson_session.cancel({canceler: user, message: 'meh', slot: booking.default_slot.id, update_all: false}) lesson_session.cancel({canceler: user, message: 'meh', slot: booking.default_slot.id, update_all: false})
lesson_session.errors.any?.should be_false lesson_session.errors.any?.should be_false
lesson_session.status.should eql LessonSession::STATUS_CANCELED
lesson_session.reload lesson_session.reload
lesson_session.status.should eql LessonSession::STATUS_CANCELED
booking.reload booking.reload
booking.status.should eql LessonSession::STATUS_CANCELED booking.status.should eql LessonSession::STATUS_CANCELED
UserMailer.deliveries.length.should eql 2 UserMailer.deliveries.length.should eql 2
@ -784,8 +743,8 @@ describe LessonBooking do
Timecop.freeze(7.days.ago) Timecop.freeze(7.days.ago)
lesson_session.cancel({canceler: user, message: 'meh', slot: booking.default_slot.id, update_all: true}) lesson_session.cancel({canceler: user, message: 'meh', slot: booking.default_slot.id, update_all: true})
lesson_session.errors.any?.should be_false lesson_session.errors.any?.should be_false
lesson_session.status.should eql LessonSession::STATUS_CANCELED
lesson_session.reload lesson_session.reload
lesson_session.status.should eql LessonSession::STATUS_CANCELED
booking.reload booking.reload
booking.status.should eql LessonSession::STATUS_CANCELED booking.status.should eql LessonSession::STATUS_CANCELED
booking.canceler.should eql user booking.canceler.should eql user

View File

@ -13,6 +13,32 @@ describe LessonSessionAnalyser do
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] } let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
describe "analyse" do describe "analyse" do
after{
Timecop.return
}
it "neither show" do
lesson = testdrive_lesson(user, teacher)
music_session = lesson.music_session
Timecop.freeze((lesson.music_session.scheduled_start + lesson.duration * 60) + 1)
analysis = LessonSessionAnalyser.analyse(lesson)
analysis[:reason].should eql LessonSessionAnalyser::TEACHER_FAULT
analysis[:student].should eql nil
analysis[:bill].should be false
student = analysis[:student_analysis]
student[:joined_on_time].should be false
student[:joined_late].should be false
student[:waited_correctly].should be false
student[:initial_waiting_pct].should eql nil
student[:potential_waiting_time].should eql nil
together = analysis[:together_analysis]
together[:session_time].should eql 0
end
it "teacher joined on time, waited, student no show" do it "teacher joined on time, waited, student no show" do
lesson = testdrive_lesson(user, teacher) lesson = testdrive_lesson(user, teacher)
music_session = lesson.music_session music_session = lesson.music_session
@ -21,8 +47,7 @@ describe LessonSessionAnalyser do
start = lesson.scheduled_start start = lesson.scheduled_start
end_time = lesson.scheduled_start + (60 * lesson.duration) end_time = lesson.scheduled_start + (60 * lesson.duration)
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher, history: music_session, created_at: start, session_removed_at: end_time) uh2 = FactoryGirl.create(:music_session_user_history, user: teacher, history: music_session, created_at: start, session_removed_at: end_time)
lesson.music_session.session_removed_at = end_time Timecop.travel(end_time + 1)
lesson.music_session.save!
analysis = LessonSessionAnalyser.analyse(lesson) analysis = LessonSessionAnalyser.analyse(lesson)
@ -41,6 +66,36 @@ describe LessonSessionAnalyser do
together[:session_time].should eql 0 together[:session_time].should eql 0
end end
it "student joined 1 min before start time, is waiting for 12 minutes" do
lesson = testdrive_lesson(user, teacher)
music_session = lesson.music_session
# create some bogus, super-perfect teacher/student times
start = lesson.scheduled_start
end_time = lesson.scheduled_start + (60 * lesson.duration)
uh2 = FactoryGirl.create(:music_session_user_history, user: user, history: music_session, created_at: start - 60, session_removed_at: nil)
Timecop.freeze(start + 11 * 60)
analysis = LessonSessionAnalyser.analyse(lesson, true)
analysis[:reason].should eql LessonSessionAnalyser::TEACHER_FAULT
analysis[:teacher].should eql LessonSessionAnalyser::NO_SHOW
analysis[:student].should be_nil
analysis[:bill].should be false
student = analysis[:student_analysis]
student[:joined_on_time].should be true
student[:joined_late].should be false
student[:waited_correctly].should be true
student[:initial_waiting_pct].should eql 1.0
student[:potential_waiting_time].should eql 600.0
student[:session_time].should eql (11 * 60).to_f
together = analysis[:together_analysis]
together[:session_time].should eql 0
end
it "teacher joined on time, waited, student joined late" do it "teacher joined on time, waited, student joined late" do
lesson = testdrive_lesson(user, teacher) lesson = testdrive_lesson(user, teacher)
music_session = lesson.music_session music_session = lesson.music_session
@ -52,12 +107,10 @@ describe LessonSessionAnalyser do
uh1 = FactoryGirl.create(:music_session_user_history, user: user, history: music_session, created_at: late_start, session_removed_at: late_start + 4 * 60) uh1 = FactoryGirl.create(:music_session_user_history, user: user, history: music_session, created_at: late_start, session_removed_at: late_start + 4 * 60)
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher, history: music_session, created_at: start, session_removed_at: end_time) uh2 = FactoryGirl.create(:music_session_user_history, user: teacher, history: music_session, created_at: start, session_removed_at: end_time)
lesson.music_session.session_removed_at = end_time Timecop.travel(end_time + 1)
lesson.music_session.save!
analysis = LessonSessionAnalyser.analyse(lesson) analysis = LessonSessionAnalyser.analyse(lesson)
puts analysis
analysis[:reason].should eql LessonSessionAnalyser::STUDENT_FAULT analysis[:reason].should eql LessonSessionAnalyser::STUDENT_FAULT
analysis[:student].should eql LessonSessionAnalyser::JOINED_LATE analysis[:student].should eql LessonSessionAnalyser::JOINED_LATE
analysis[:bill].should be true analysis[:bill].should be true
@ -92,8 +145,7 @@ describe LessonSessionAnalyser do
analysis = LessonSessionAnalyser.analyse(lesson) analysis = LessonSessionAnalyser.analyse(lesson)
analysis[:reason].should eql LessonSessionAnalyser::SESSION_ONGOING analysis[:reason].should eql LessonSessionAnalyser::SESSION_ONGOING
lesson.music_session.session_removed_at = end_time Timecop.travel(end_time + 1)
lesson.music_session.save!
analysis = LessonSessionAnalyser.analyse(lesson) analysis = LessonSessionAnalyser.analyse(lesson)
analysis[:reason].should eql LessonSessionAnalyser::SUCCESS analysis[:reason].should eql LessonSessionAnalyser::SUCCESS

View File

@ -2,12 +2,12 @@ require 'spec_helper'
describe LessonSession do describe LessonSession do
let(:user) {FactoryGirl.create(:user, stored_credit_card: false, remaining_free_lessons: 1, remaining_test_drives: 1)} let(:user) {FactoryGirl.create(:user, stored_credit_card: true, remaining_free_lessons: 1, remaining_test_drives: 1)}
let(:teacher) {FactoryGirl.create(:teacher_user)} let(:teacher) {FactoryGirl.create(:teacher_user)}
let(:slot1) { FactoryGirl.build(:lesson_booking_slot_single) } let(:slot1) { FactoryGirl.build(:lesson_booking_slot_single) }
let(:slot2) { FactoryGirl.build(:lesson_booking_slot_single) } let(:slot2) { FactoryGirl.build(:lesson_booking_slot_single) }
let(:lesson_booking) {LessonBooking.book_normal(user, teacher, [slot1, slot2], "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60)} let(:lesson_booking) {b = LessonBooking.book_normal(user, teacher, [slot1, slot2], "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60); b.card_presumed_ok = true; b.save!; b}
let(:lesson_session) {lesson_booking.lesson_sessions[0]} let(:lesson_session) {lesson_booking.lesson_sessions[0]}
describe "accept" do describe "accept" do
@ -16,10 +16,28 @@ describe LessonSession do
end end
end end
describe "upcoming_sessions_reminder" do
it "succeeds" do
UserMailer.deliveries.clear
LessonSession.upcoming_sessions_reminder
lesson_session.touch
lesson_session.sent_starting_notice.should be_false
lesson_session.is_requested?.should be_true
lesson_session.music_session.scheduled_start = 15.minutes.from_now
lesson_session.music_session.save!
LessonSession.upcoming_sessions_reminder
UserMailer.deliveries.count.should eql 2
UserMailer.deliveries.clear
lesson_session.reload
lesson_session.sent_starting_notice.should be_true
LessonSession.upcoming_sessions_reminder
UserMailer.deliveries.count.should eql 0
end
end
describe "index" do describe "index" do
it "finds single lesson as student" do it "finds single lesson as student" do
lesson_booking.touch
lesson_session.touch lesson_session.touch
lesson_session.music_session.creator.should eql lesson_session.lesson_booking.user lesson_session.music_session.creator.should eql lesson_session.lesson_booking.user
lesson_session.lesson_booking.teacher.should eql teacher lesson_session.lesson_booking.teacher.should eql teacher

View File

@ -362,6 +362,31 @@ describe MusicSession do
end end
describe "scheduled" do describe "scheduled" do
it "includes any RSVP'ed" do
rsvp_request = FactoryGirl.create(:rsvp_request_for_multiple_slots, user: some_user, music_session: music_session1, number: 2, chosen:true)
approved_rsvps = music_session1.approved_rsvps
approved_rsvps.length.should == 2
sessions = MusicSession.scheduled(approved_rsvps[0])
sessions.length.should == 1
sessions = MusicSession.scheduled(approved_rsvps[1])
sessions.length.should == 1
end
it "includes invited" do
invitee = FactoryGirl.create(:user, last_jam_audio_latency: 30, last_jam_locidispid: 3)
FactoryGirl.create(:friendship, user: creator, friend: invitee)
FactoryGirl.create(:friendship, user: invitee, friend: creator)
music_session = FactoryGirl.create(:music_session, creator: creator)
FactoryGirl.create(:invitation, receiver:invitee, sender:creator, music_session: music_session)
sessions = MusicSession.scheduled(invitee)
sessions.length.should == 1
end
it "excludes based on time-range" do it "excludes based on time-range" do
session = FactoryGirl.create(:music_session, scheduled_start: Time.now) session = FactoryGirl.create(:music_session, scheduled_start: Time.now)

View File

@ -9,6 +9,9 @@ describe Sale do
let(:jam_track3) { FactoryGirl.create(:jam_track) } let(:jam_track3) { FactoryGirl.create(:jam_track) }
let(:gift_card) { GiftCardType.jam_track_5 } let(:gift_card) { GiftCardType.jam_track_5 }
before(:each) {
Timecop.return
}
def assert_free_line_item(sale_line_item, jamtrack) def assert_free_line_item(sale_line_item, jamtrack)
sale_line_item.recurly_tax_in_cents.should be_nil sale_line_item.recurly_tax_in_cents.should be_nil
sale_line_item.recurly_total_in_cents.should be_nil sale_line_item.recurly_total_in_cents.should be_nil
@ -596,6 +599,8 @@ describe Sale do
lesson_session.music_session.session_removed_at = end_time lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save! lesson_session.music_session.save!
Timecop.travel(end_time + 1)
# bill the user # bill the user
LessonSession.hourly_check LessonSession.hourly_check
@ -649,6 +654,8 @@ describe Sale do
lesson_session.music_session.session_removed_at = end_time lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save! lesson_session.music_session.save!
Timecop.travel(end_time + 1)
# bill the user # bill the user
LessonSession.hourly_check LessonSession.hourly_check
@ -715,7 +722,7 @@ describe Sale do
booking.should eql user.unprocessed_test_drive booking.should eql user.unprocessed_test_drive
token = create_stripe_token token = create_stripe_token
result = user.payment_update({token: token, zip: '72205', test_drive: true}) result = user.payment_update({token: token, zip: '72205', test_drive: true, booking_id: booking.id})
booking.reload booking.reload
booking.card_presumed_ok.should be_true booking.card_presumed_ok.should be_true
@ -731,7 +738,7 @@ describe Sale do
line_item = sale.sale_line_items[0] line_item = sale.sale_line_items[0]
line_item.quantity.should eql 1 line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.test_drive.id line_item.product_id.should eq LessonPackageType.test_drive_4.id
user.reload user.reload
user.stripe_customer_id.should_not be nil user.stripe_customer_id.should_not be nil
@ -762,7 +769,7 @@ describe Sale do
user.remaining_test_drives.should eql 0 user.remaining_test_drives.should eql 0
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, booking_id: booking.id})
booking.reload booking.reload
booking.card_presumed_ok.should be_true booking.card_presumed_ok.should be_true
@ -778,7 +785,7 @@ describe Sale do
line_item = sale.sale_line_items[0] line_item = sale.sale_line_items[0]
line_item.quantity.should eql 1 line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.test_drive.id line_item.product_id.should eq LessonPackageType.test_drive_4.id
user.reload user.reload
@ -819,7 +826,7 @@ describe Sale do
line_item = sale.sale_line_items[0] line_item = sale.sale_line_items[0]
line_item.quantity.should eql 1 line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.test_drive.id line_item.product_id.should eq LessonPackageType.test_drive_4.id
end end
it "will reject second test drive purchase" do it "will reject second test drive purchase" do

View File

@ -0,0 +1,23 @@
require 'spec_helper'
describe TeacherDistribution do
let(:teacher) {FactoryGirl.create(:teacher_user)}
describe "index" do
it "empty" do
TeacherDistribution.index(teacher, {})[:query].count.should eql 0
end
it "returns single" do
distribution = FactoryGirl.create(:teacher_distribution, teacher: teacher)
TeacherDistribution.index(teacher, {})[:query].count.should eql 1
distribution = FactoryGirl.create(:teacher_distribution) # some random teacher
TeacherDistribution.index(teacher, {})[:query].count.should eql 1
end
end
end

View File

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

View File

@ -842,6 +842,45 @@ describe User do
end end
end end
describe "uncollectables" do
let(:user) {FactoryGirl.create(:user)}
let(:teacher) {FactoryGirl.create(:teacher_user)}
it "empty" do
user.uncollectables.count.should eql 0
end
it "one" do
lesson_session = normal_lesson(user, teacher)
lesson_session.lesson_payment_charge.user.should eql user
lesson_session.lesson_payment_charge.billing_attempts = 1
lesson_session.lesson_payment_charge.save!
uncollectables = user.uncollectables
uncollectables.count.should eql 1
uncollectable = uncollectables[0]
uncollectable.description.should_not be_nil
uncollectable.expected_price_in_cents.should eql 3000
uncollectable.is_card_declined?.should be_false
end
it "for monthly" do
lesson_session = monthly_lesson(user, teacher)
lesson_session.booked_price.should eql 30.00
LessonBooking.hourly_check
lesson_session.lesson_payment_charge.should be_nil
purchases=LessonPackagePurchase.where(user_id: user.id)
purchases.count.should eql 1
purchases[0].lesson_payment_charge.billing_attempts = 1
purchases[0].lesson_payment_charge.save!
uncollectables = user.uncollectables
uncollectables.count.should eql 1
uncollectable = uncollectables[0]
uncollectable.description.should_not be_nil
uncollectable.expected_price_in_cents.should eql 3000
uncollectable.is_card_declined?.should be_false
end
end
=begin =begin
describe "update avatar" do describe "update avatar" do

View File

@ -27,6 +27,7 @@ describe "RenderMailers", :slow => true do
it { @filename="welcome_message"; UserMailer.welcome_message(user).deliver } it { @filename="welcome_message"; UserMailer.welcome_message(user).deliver }
it { @filename="student_welcome_message"; UserMailer.student_welcome_message(user).deliver } it { @filename="student_welcome_message"; UserMailer.student_welcome_message(user).deliver }
it { @filename="school_owner_welcome_message"; UserMailer.school_owner_welcome_message(user).deliver }
it { @filename="confirm_email"; UserMailer.confirm_email(user, "/signup").deliver } it { @filename="confirm_email"; UserMailer.confirm_email(user, "/signup").deliver }
it { @filename="password_reset"; UserMailer.password_reset(user, '/reset_password').deliver } it { @filename="password_reset"; UserMailer.password_reset(user, '/reset_password').deliver }
it { @filename="password_changed"; UserMailer.password_changed(user).deliver } it { @filename="password_changed"; UserMailer.password_changed(user).deliver }
@ -49,6 +50,7 @@ describe "RenderMailers", :slow => true do
@filename = "teacher_welcome_message" @filename = "teacher_welcome_message"
UserMailer.teacher_welcome_message(teacher).deliver UserMailer.teacher_welcome_message(teacher).deliver
end end
it "teacher_lesson_request" do it "teacher_lesson_request" do
@filename = "teacher_lesson_request" @filename = "teacher_lesson_request"
@ -120,6 +122,22 @@ describe "RenderMailers", :slow => true do
UserMailer.deliveries.clear UserMailer.deliveries.clear
UserMailer.teacher_lesson_completed(lesson).deliver UserMailer.teacher_lesson_completed(lesson).deliver
end end
it "lesson_starting_soon_teacher" do
@filename = "lesson_starting_soon_teacher"
lesson = testdrive_lesson(user, teacher)
UserMailer.deliveries.clear
UserMailer.lesson_starting_soon_teacher(lesson).deliver
end
it "lesson_starting_soon_student" do
@filename = "lesson_starting_soon_student"
lesson = testdrive_lesson(user, teacher)
UserMailer.deliveries.clear
UserMailer.lesson_starting_soon_student(lesson).deliver
end
end end
end end

View File

@ -33,7 +33,7 @@ def testdrive_lesson(user, teacher, slots = nil)
booking.card_presumed_ok.should be_true booking.card_presumed_ok.should be_true
if user.most_recent_test_drive_purchase.nil? if user.most_recent_test_drive_purchase.nil?
LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive) LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive_4)
end end
lesson.accept({message: 'Yeah I got this', slot: slots[0]}) lesson.accept({message: 'Yeah I got this', slot: slots[0]})
@ -66,7 +66,7 @@ def normal_lesson(user, teacher, slots = nil)
booking.card_presumed_ok.should be_true booking.card_presumed_ok.should be_true
#if user.most_recent_test_drive_purchase.nil? #if user.most_recent_test_drive_purchase.nil?
# LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive) # LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive_4)
#end #end
lesson.accept({message: 'Yeah I got this', slot: slots[0]}) lesson.accept({message: 'Yeah I got this', slot: slots[0]})
@ -74,6 +74,41 @@ def normal_lesson(user, teacher, slots = nil)
lesson.reload lesson.reload
lesson.slot.should eql slots[0] lesson.slot.should eql slots[0]
lesson.status.should eql LessonSession::STATUS_APPROVED lesson.status.should eql LessonSession::STATUS_APPROVED
lesson.music_session.should_not be_nil
lesson
end
def monthly_lesson(user, teacher, slots = nil)
if slots.nil?
slots = []
slots << FactoryGirl.build(:lesson_booking_slot_recurring)
slots << FactoryGirl.build(:lesson_booking_slot_recurring)
end
if user.stored_credit_card == false
user.stored_credit_card = true
user.save!
end
booking = LessonBooking.book_normal(user, teacher, slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60)
# puts "NORMAL BOOKING #{booking.errors.inspect}"
booking.errors.any?.should be_false
lesson = booking.lesson_sessions[0]
booking.card_presumed_ok.should be_true
#if user.most_recent_test_drive_purchase.nil?
# LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive_4)
#end
lesson.accept({message: 'Yeah I got this', slot: slots[0]})
lesson.errors.any?.should be_false
lesson.reload
lesson.slot.should eql slots[0]
lesson.status.should eql LessonSession::STATUS_APPROVED
lesson.music_session.should_not be_nil
lesson lesson
end end

View File

@ -7,6 +7,10 @@ def app_config
'http://localhost:3333' 'http://localhost:3333'
end end
def email_partners_alias
'partners@jamkazam.com'
end
def email_social_alias def email_social_alias
'social@jamkazam.com' 'social@jamkazam.com'
end end

View File

@ -19,6 +19,7 @@ else
end end
#gem 'license_finder' #gem 'license_finder'
gem 'pg_migrate', '0.1.14'
gem 'kickbox' gem 'kickbox'
gem 'oj', '2.10.2' gem 'oj', '2.10.2'
gem 'builder' gem 'builder'
@ -89,7 +90,7 @@ gem 'htmlentities'
gem 'sanitize' gem 'sanitize'
gem 'recurly' gem 'recurly'
#gem 'guard', '2.7.3' #gem 'guard', '2.7.3'
gem 'influxdb' #, '0.1.8' #gem 'influxdb' #, '0.1.8'
gem 'cause' # needed by influxdb gem 'cause' # needed by influxdb
gem 'influxdb-rails'# , '0.1.10' gem 'influxdb-rails'# , '0.1.10'
gem 'sitemap_generator' gem 'sitemap_generator'
@ -142,6 +143,7 @@ group :test, :cucumber do
gem 'simplecov', '~> 0.7.1' gem 'simplecov', '~> 0.7.1'
gem 'simplecov-rcov' gem 'simplecov-rcov'
gem 'capybara', '2.4.4' gem 'capybara', '2.4.4'
gem 'rails-assets-sinon', source: 'https://rails-assets.org'
#if ENV['JAMWEB_QT5'] == '1' #if ENV['JAMWEB_QT5'] == '1'
# # necessary on platforms such as arch linux, where pacman -S qt5-webkit is your easiet option # # necessary on platforms such as arch linux, where pacman -S qt5-webkit is your easiet option
# gem "capybara-webkit", :git => 'git://github.com/thoughtbot/capybara-webkit.git' # gem "capybara-webkit", :git => 'git://github.com/thoughtbot/capybara-webkit.git'
@ -159,6 +161,7 @@ group :test, :cucumber do
# gem 'growl', '1.0.3' # gem 'growl', '1.0.3'
gem 'poltergeist' gem 'poltergeist'
gem 'resque_spec' gem 'resque_spec'
gem 'timecop'
#gem 'thin' #gem 'thin'
end end

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Some files were not shown because too many files have changed in this diff Show More