Merge with develop.
This commit is contained in:
parent
d60eff50ba
commit
d5a8959677
|
|
@ -2,48 +2,47 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
|
||||||
|
|
||||||
menu :label => 'Partners', :parent => 'Affiliates'
|
menu :label => 'Partners', :parent => 'Affiliates'
|
||||||
|
|
||||||
config.sort_order = 'created_at DESC'
|
config.sort_order = 'referral_user_count DESC'
|
||||||
config.batch_actions = false
|
config.batch_actions = false
|
||||||
# config.clear_action_items!
|
# config.clear_action_items!
|
||||||
config.filters = false
|
config.filters = false
|
||||||
|
|
||||||
form :partial => 'form'
|
form :partial => 'form'
|
||||||
|
|
||||||
|
scope("Active", default: true) { |scope| scope.where('partner_user_id IS NOT NULL') }
|
||||||
|
scope("Unpaid") { |partner| partner.unpaid }
|
||||||
|
|
||||||
index do
|
index do
|
||||||
column 'User' do |oo| link_to(oo.partner_user.name, "http://www.jamkazam.com/client#/profile/#{oo.partner_user.id}", {:title => oo.partner_user.name}) end
|
|
||||||
column 'Email' do |oo| oo.partner_user.email end
|
# default_actions # use this for all view/edit/delete links
|
||||||
|
|
||||||
|
column 'User' do |oo| link_to(oo.partner_user.name, admin_user_path(oo.partner_user.id), {:title => oo.partner_user.name}) end
|
||||||
column 'Name' do |oo| oo.partner_name end
|
column 'Name' do |oo| oo.partner_name end
|
||||||
column 'Code' do |oo| oo.partner_code end
|
column 'Type' do |oo| oo.entity_type end
|
||||||
|
column 'Code' do |oo| oo.id end
|
||||||
column 'Referral Count' do |oo| oo.referral_user_count end
|
column 'Referral Count' do |oo| oo.referral_user_count end
|
||||||
# column 'Referrals' do |oo| link_to('View', admin_referrals_path(AffiliatePartner::PARAM_REFERRAL => oo.id)) end
|
column 'Earnings' do |oo| sprintf("$%.2f", oo.cumulative_earnings_in_dollars) end
|
||||||
|
column 'Amount Owed' do |oo| sprintf("$%.2f", oo.due_amount_in_cents.to_f / 100.to_f) end
|
||||||
|
column 'Pay Actions' do |oo|
|
||||||
|
link_to('Mark Paid', mark_paid_admin_affiliate_path(oo.id), :confirm => "Mark this affiliate as PAID?") if oo.unpaid
|
||||||
|
end
|
||||||
|
|
||||||
default_actions
|
default_actions
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
action_item :only => [:show] do
|
||||||
|
link_to("Mark Paid",
|
||||||
|
mark_paid_admin_affiliate_path(resource.id),
|
||||||
|
:confirm => "Mark this affiliate as PAID?") if resource.unpaid
|
||||||
|
end
|
||||||
|
|
||||||
|
member_action :mark_paid, :method => :get do
|
||||||
|
resource.mark_paid
|
||||||
|
redirect_to admin_affiliate_path(resource.id)
|
||||||
|
end
|
||||||
|
|
||||||
controller do
|
controller do
|
||||||
|
|
||||||
def show
|
|
||||||
redirect_to admin_referrals_path(AffiliatePartner::PARAM_REFERRAL => resource.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
obj = AffiliatePartner.create_with_params(params[:jam_ruby_affiliate_partner])
|
|
||||||
if obj.errors.present?
|
|
||||||
set_resource_ivar(obj)
|
|
||||||
render active_admin_template('new')
|
|
||||||
else
|
|
||||||
redirect_to admin_affiliates_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
obj = resource
|
|
||||||
vals = params[:jam_ruby_affiliate_partner]
|
|
||||||
obj.partner_name = vals[:partner_name]
|
|
||||||
obj.user_email = vals[:user_email] if vals[:user_email].present?
|
|
||||||
obj.save!
|
|
||||||
set_resource_ivar(obj)
|
|
||||||
render active_admin_template('show')
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
ActiveAdmin.register JamRuby::FraudAlert, :as => 'Fraud Alerts' do
|
||||||
|
|
||||||
|
menu :label => 'Fraud Alerts', :parent => 'JamTracks'
|
||||||
|
|
||||||
|
config.sort_order = 'created_at desc'
|
||||||
|
config.batch_actions = false
|
||||||
|
|
||||||
|
|
||||||
|
scope("Not Whitelisted", default:true) { |scope|
|
||||||
|
scope.joins('INNER JOIN "machine_fingerprints" ON "machine_fingerprints"."id" = "fraud_alerts"."machine_fingerprint_id" LEFT OUTER JOIN "fingerprint_whitelists" ON "fingerprint_whitelists"."fingerprint" = "machine_fingerprints"."fingerprint"').where('fingerprint_whitelists IS NULL')}
|
||||||
|
|
||||||
|
index do
|
||||||
|
default_actions
|
||||||
|
|
||||||
|
column :machine_fingerprint
|
||||||
|
column :user
|
||||||
|
column :created_at
|
||||||
|
column :resolved
|
||||||
|
|
||||||
|
column "" do |alert|
|
||||||
|
link_to 'Matching MAC', "fraud_alerts/#{alert.id}/same_fingerprints"
|
||||||
|
end
|
||||||
|
column "" do |alert|
|
||||||
|
link_to 'Matching MAC and IP Address', "fraud_alerts/#{alert.id}/same_fingerprints_and_ip"
|
||||||
|
end
|
||||||
|
column "" do |alert|
|
||||||
|
link_to 'Matching IP Address', "fraud_alerts/#{alert.id}/same_ip"
|
||||||
|
end
|
||||||
|
column "" do |alert|
|
||||||
|
link_to 'Resolve', "fraud_alerts/#{alert.id}/resolve"
|
||||||
|
end
|
||||||
|
column "" do |alert|
|
||||||
|
link_to 'Whitelist Similar', "fraud_alerts/#{alert.id}/whitelist"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
member_action :same_fingerprints, :method => :get do
|
||||||
|
alert = FraudAlert.find(params[:id])
|
||||||
|
|
||||||
|
redirect_to admin_machine_fingerprints_path("q[fingerprint_equals]" => alert.machine_fingerprint.fingerprint, commit: 'Filter', order: 'created_at_desc')
|
||||||
|
end
|
||||||
|
|
||||||
|
member_action :same_fingerprints_and_ip, :method => :get do
|
||||||
|
alert = FraudAlert.find(params[:id])
|
||||||
|
|
||||||
|
redirect_to admin_machine_fingerprints_path("q[fingerprint_equals]" => alert.machine_fingerprint.fingerprint, "q[remote_ip_equals]" => alert.machine_fingerprint.remote_ip, commit: 'Filter', order: 'created_at_desc')
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
member_action :resolve, :method => :get do
|
||||||
|
alert = FraudAlert.find(params[:id])
|
||||||
|
alert.resolved = true
|
||||||
|
alert.save!
|
||||||
|
|
||||||
|
redirect_to admin_fraud_alerts_path, notice: "That fraud alert has been marked as resolved"
|
||||||
|
end
|
||||||
|
|
||||||
|
member_action :whitelist, :method => :get do
|
||||||
|
alert = FraudAlert.find(params[:id])
|
||||||
|
|
||||||
|
wl = FingerprintWhitelist.new
|
||||||
|
wl.fingerprint = alert.machine_fingerprint.fingerprint
|
||||||
|
success = wl.save
|
||||||
|
|
||||||
|
redirect_to admin_fraud_alerts_path, notice: success ? "Added #{alert.machine_fingerprint.fingerprint} to whitelist" : "Could not add #{alert.machine_fingerprint.fingerprint} to whiteliste"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
ActiveAdmin.register JamRuby::MachineExtra, :as => 'Machine Extra' do
|
||||||
|
|
||||||
|
menu :label => 'Machine Extra', :parent => 'JamTracks'
|
||||||
|
|
||||||
|
config.sort_order = 'created_at desc'
|
||||||
|
config.batch_actions = false
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
ActiveAdmin.register JamRuby::MachineFingerprint, :as => 'Machine Fingerprints' do
|
||||||
|
|
||||||
|
menu :label => 'Machine Fingerprints', :parent => 'JamTracks'
|
||||||
|
|
||||||
|
config.sort_order = 'created_at desc'
|
||||||
|
config.batch_actions = false
|
||||||
|
|
||||||
|
index do
|
||||||
|
column :user
|
||||||
|
column 'Hash' do |fp|
|
||||||
|
fp.fingerprint
|
||||||
|
end
|
||||||
|
column :remote_ip
|
||||||
|
column 'Detail' do |fp|
|
||||||
|
detail = fp.detail
|
||||||
|
if detail
|
||||||
|
detail.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
column :created_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
ActiveAdmin.register JamRuby::MusicSessionComment, :as => 'Ratings' do
|
||||||
|
|
||||||
|
config.per_page = 150
|
||||||
|
config.clear_action_items!
|
||||||
|
config.sort_order = 'created_at_desc'
|
||||||
|
menu :parent => 'Sessions', :label => 'Ratings'
|
||||||
|
|
||||||
|
index do
|
||||||
|
column :comment
|
||||||
|
column :user
|
||||||
|
column :created_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
ActiveAdmin.register JamRuby::SessionInfoComment, :as => 'Comments' do
|
||||||
|
|
||||||
|
config.per_page = 50
|
||||||
|
config.clear_action_items!
|
||||||
|
config.sort_order = 'created_at_desc'
|
||||||
|
menu :parent => 'Sessions', :label => 'Comments'
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -1,13 +1,7 @@
|
||||||
<%= semantic_form_for([:admin, resource], :url => resource.new_record? ? admin_affiliates_path : "/admin/affiliates/#{resource.id}") do |f| %>
|
<%= semantic_form_for([:admin, resource], :url => resource.new_record? ? admin_affiliates_path : "/admin/affiliates/#{resource.id}") do |f| %>
|
||||||
<%= f.semantic_errors *f.object.errors.keys %>
|
<%= f.semantic_errors *f.object.errors.keys %>
|
||||||
<%= f.inputs do %>
|
<%= f.inputs do %>
|
||||||
<%= f.input(:user_email, :input_html => {:maxlength => 255}) %>
|
|
||||||
<%= f.input(:partner_name, :input_html => {:maxlength => 128}) %>
|
<%= f.input(:partner_name, :input_html => {:maxlength => 128}) %>
|
||||||
<% if resource.new_record? %>
|
|
||||||
<%= f.input(:partner_code, :input_html => {:maxlength => 128}) %>
|
|
||||||
<% else %>
|
|
||||||
<%= f.input(:partner_code, :input_html => {:maxlength => 128, :readonly => 'readonly'}) %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= f.actions %>
|
<%= f.actions %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
||||||
|
|
@ -289,5 +289,8 @@ payment_history.sql
|
||||||
jam_track_right_private_key.sql
|
jam_track_right_private_key.sql
|
||||||
first_downloaded_jamtrack_at.sql
|
first_downloaded_jamtrack_at.sql
|
||||||
signing.sql
|
signing.sql
|
||||||
enhance_band_profile.sql
|
|
||||||
optimized_redeemption.sql
|
optimized_redeemption.sql
|
||||||
|
optimized_redeemption.sql
|
||||||
|
optimized_redemption_warn_mode.sql
|
||||||
|
affiliate_partners2.sql
|
||||||
|
enhance_band_profile.sql
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
CREATE TABLE affiliate_legalese (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
legalese TEXT,
|
||||||
|
version INTEGER NOT NULL DEFAULT 1,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE users DROP CONSTRAINT users_affiliate_referral_id_fkey;
|
||||||
|
ALTER TABLE users DROP COLUMN affiliate_referral_id;
|
||||||
|
DROP TABLE affiliate_partners;
|
||||||
|
|
||||||
|
CREATE TABLE affiliate_partners (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
partner_name VARCHAR(1000),
|
||||||
|
partner_user_id VARCHAR(64) REFERENCES users(id) ON DELETE SET NULL,
|
||||||
|
entity_type VARCHAR(64),
|
||||||
|
legalese_id VARCHAR(64),
|
||||||
|
signed_at TIMESTAMP,
|
||||||
|
last_paid_at TIMESTAMP,
|
||||||
|
address JSON NOT NULL DEFAULT '{}',
|
||||||
|
tax_identifier VARCHAR(1000),
|
||||||
|
referral_user_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
cumulative_earnings_in_cents INTEGER NOT NULL DEFAULT 0,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
CREATE SEQUENCE partner_key_sequence;
|
||||||
|
ALTER SEQUENCE partner_key_sequence RESTART WITH 10000;
|
||||||
|
ALTER TABLE affiliate_partners ALTER COLUMN id SET DEFAULT nextval('partner_key_sequence');;
|
||||||
|
|
||||||
|
ALTER TABLE users ADD COLUMN affiliate_referral_id INTEGER REFERENCES affiliate_partners(id) ON DELETE SET NULL;
|
||||||
|
CREATE INDEX affiliate_partners_legalese_idx ON affiliate_partners(legalese_id);
|
||||||
|
|
||||||
|
CREATE UNLOGGED TABLE affiliate_referral_visits (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
affiliate_partner_id INTEGER NOT NULL,
|
||||||
|
ip_address VARCHAR NOT NULL,
|
||||||
|
visited_url VARCHAR,
|
||||||
|
referral_url VARCHAR,
|
||||||
|
first_visit BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
user_id VARCHAR(64),
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX on affiliate_referral_visits (affiliate_partner_id, created_at);
|
||||||
|
|
||||||
|
CREATE TABLE affiliate_quarterly_payments (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
quarter INTEGER NOT NULL,
|
||||||
|
year INTEGER NOT NULL,
|
||||||
|
affiliate_partner_id INTEGER NOT NULL REFERENCES affiliate_partners(id),
|
||||||
|
due_amount_in_cents INTEGER NOT NULL DEFAULT 0,
|
||||||
|
paid BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
closed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
jamtracks_sold INTEGER NOT NULL DEFAULT 0,
|
||||||
|
closed_at TIMESTAMP,
|
||||||
|
paid_at TIMESTAMP,
|
||||||
|
last_updated TIMESTAMP,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE affiliate_monthly_payments (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
month INTEGER NOT NULL,
|
||||||
|
year INTEGER NOT NULL,
|
||||||
|
affiliate_partner_id INTEGER NOT NULL REFERENCES affiliate_partners(id),
|
||||||
|
due_amount_in_cents INTEGER NOT NULL DEFAULT 0,
|
||||||
|
closed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
jamtracks_sold INTEGER NOT NULL DEFAULT 0,
|
||||||
|
closed_at TIMESTAMP,
|
||||||
|
last_updated TIMESTAMP,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE INDEX ON affiliate_quarterly_payments (affiliate_partner_id, year, quarter);
|
||||||
|
CREATE UNIQUE INDEX ON affiliate_quarterly_payments (year, quarter, affiliate_partner_id);
|
||||||
|
CREATE UNIQUE INDEX ON affiliate_monthly_payments (year, month, affiliate_partner_id);
|
||||||
|
CREATE INDEX ON affiliate_monthly_payments (affiliate_partner_id, year, month);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE sale_line_items ADD COLUMN affiliate_referral_id INTEGER REFERENCES affiliate_partners(id);
|
||||||
|
ALTER TABLE sale_line_items ADD COLUMN affiliate_referral_fee_in_cents INTEGER;
|
||||||
|
ALTER TABLE sale_line_items ADD COLUMN affiliate_refunded BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE sale_line_items ADD COLUMN affiliate_refunded_at TIMESTAMP;
|
||||||
|
ALTER TABLE generic_state ADD COLUMN affiliate_tallied_at TIMESTAMP;
|
||||||
|
|
||||||
|
CREATE TABLE affiliate_traffic_totals (
|
||||||
|
day DATE NOT NULL,
|
||||||
|
signups INTEGER NOT NULL DEFAULT 0,
|
||||||
|
visits INTEGER NOT NULL DEFAULT 0,
|
||||||
|
affiliate_partner_id INTEGER NOT NULL REFERENCES affiliate_partners(id),
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX ON affiliate_traffic_totals (day, affiliate_partner_id);
|
||||||
|
CREATE INDEX ON affiliate_traffic_totals (affiliate_partner_id, day);
|
||||||
|
|
||||||
|
CREATE VIEW affiliate_payments AS
|
||||||
|
SELECT id AS monthly_id,
|
||||||
|
CAST(NULL as VARCHAR) AS quarterly_id,
|
||||||
|
affiliate_partner_id,
|
||||||
|
due_amount_in_cents,
|
||||||
|
jamtracks_sold,
|
||||||
|
created_at,
|
||||||
|
closed,
|
||||||
|
CAST(NULL AS BOOLEAN) AS paid,
|
||||||
|
year,
|
||||||
|
month as month,
|
||||||
|
CAST(NULL AS INTEGER) as quarter,
|
||||||
|
month as time_sort,
|
||||||
|
'monthly' AS payment_type
|
||||||
|
FROM affiliate_monthly_payments
|
||||||
|
UNION ALL
|
||||||
|
SELECT CAST(NULL as VARCHAR) AS monthly_id,
|
||||||
|
id AS quarterly_id,
|
||||||
|
affiliate_partner_id,
|
||||||
|
due_amount_in_cents,
|
||||||
|
jamtracks_sold,
|
||||||
|
created_at,
|
||||||
|
closed,
|
||||||
|
paid,
|
||||||
|
year,
|
||||||
|
CAST(NULL AS INTEGER) as month,
|
||||||
|
quarter,
|
||||||
|
(quarter * 3) + 3 as time_sort,
|
||||||
|
'quarterly' AS payment_type
|
||||||
|
FROM affiliate_quarterly_payments;
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
DROP TABLE machine_fingerprints;
|
||||||
|
|
||||||
|
CREATE TABLE machine_fingerprints (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
fingerprint VARCHAR(20000) NOT NULL,
|
||||||
|
when_taken VARCHAR NOT NULL,
|
||||||
|
print_type VARCHAR NOT NULL,
|
||||||
|
remote_ip VARCHAR(1000) NOT NULL,
|
||||||
|
jam_track_right_id BIGINT REFERENCES jam_track_rights(id) ON DELETE SET NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE machine_extras (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
machine_fingerprint_id VARCHAR(64) NOT NULL REFERENCES machine_fingerprints(id) ON DELETE CASCADE,
|
||||||
|
mac_address VARCHAR(100),
|
||||||
|
mac_name VARCHAR(255),
|
||||||
|
upstate BOOLEAN,
|
||||||
|
ipaddr_0 VARCHAR(200),
|
||||||
|
ipaddr_1 VARCHAR(200),
|
||||||
|
ipaddr_2 VARCHAR(200),
|
||||||
|
ipaddr_3 VARCHAR(200),
|
||||||
|
ipaddr_4 VARCHAR(200),
|
||||||
|
ipaddr_5 VARCHAR(200),
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE fraud_alerts (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
machine_fingerprint_id VARCHAR(64) NOT NULL REFERENCES machine_fingerprints(id) ON DELETE CASCADE,
|
||||||
|
user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
resolved BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE fingerprint_whitelists (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
fingerprint VARCHAR(20000) UNIQUE NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX machine_fingerprints_index1 ON machine_fingerprints USING btree (fingerprint, user_id, remote_ip, created_at);
|
||||||
|
|
@ -59,8 +59,9 @@ require "jam_ruby/resque/scheduled/score_history_sweeper"
|
||||||
require "jam_ruby/resque/scheduled/scheduled_music_session_cleaner"
|
require "jam_ruby/resque/scheduled/scheduled_music_session_cleaner"
|
||||||
require "jam_ruby/resque/scheduled/recordings_cleaner"
|
require "jam_ruby/resque/scheduled/recordings_cleaner"
|
||||||
require "jam_ruby/resque/scheduled/jam_tracks_cleaner"
|
require "jam_ruby/resque/scheduled/jam_tracks_cleaner"
|
||||||
require "jam_ruby/resque/jam_tracks_builder"
|
|
||||||
require "jam_ruby/resque/scheduled/stats_maker"
|
require "jam_ruby/resque/scheduled/stats_maker"
|
||||||
|
require "jam_ruby/resque/scheduled/tally_affiliates"
|
||||||
|
require "jam_ruby/resque/jam_tracks_builder"
|
||||||
require "jam_ruby/resque/google_analytics_event"
|
require "jam_ruby/resque/google_analytics_event"
|
||||||
require "jam_ruby/resque/batch_email_job"
|
require "jam_ruby/resque/batch_email_job"
|
||||||
require "jam_ruby/resque/long_running"
|
require "jam_ruby/resque/long_running"
|
||||||
|
|
@ -104,6 +105,9 @@ require "jam_ruby/models/user"
|
||||||
require "jam_ruby/models/anonymous_user"
|
require "jam_ruby/models/anonymous_user"
|
||||||
require "jam_ruby/models/signup_hint"
|
require "jam_ruby/models/signup_hint"
|
||||||
require "jam_ruby/models/machine_fingerprint"
|
require "jam_ruby/models/machine_fingerprint"
|
||||||
|
require "jam_ruby/models/machine_extra"
|
||||||
|
require "jam_ruby/models/fraud_alert"
|
||||||
|
require "jam_ruby/models/fingerprint_whitelist"
|
||||||
require "jam_ruby/models/rsvp_request"
|
require "jam_ruby/models/rsvp_request"
|
||||||
require "jam_ruby/models/rsvp_slot"
|
require "jam_ruby/models/rsvp_slot"
|
||||||
require "jam_ruby/models/rsvp_request_rsvp_slot"
|
require "jam_ruby/models/rsvp_request_rsvp_slot"
|
||||||
|
|
@ -203,6 +207,12 @@ require "jam_ruby/app/mailers/async_mailer"
|
||||||
require "jam_ruby/app/mailers/batch_mailer"
|
require "jam_ruby/app/mailers/batch_mailer"
|
||||||
require "jam_ruby/app/mailers/progress_mailer"
|
require "jam_ruby/app/mailers/progress_mailer"
|
||||||
require "jam_ruby/models/affiliate_partner"
|
require "jam_ruby/models/affiliate_partner"
|
||||||
|
require "jam_ruby/models/affiliate_legalese"
|
||||||
|
require "jam_ruby/models/affiliate_quarterly_payment"
|
||||||
|
require "jam_ruby/models/affiliate_monthly_payment"
|
||||||
|
require "jam_ruby/models/affiliate_traffic_total"
|
||||||
|
require "jam_ruby/models/affiliate_referral_visit"
|
||||||
|
require "jam_ruby/models/affiliate_payment"
|
||||||
require "jam_ruby/models/chat_message"
|
require "jam_ruby/models/chat_message"
|
||||||
require "jam_ruby/models/shopping_cart"
|
require "jam_ruby/models/shopping_cart"
|
||||||
require "jam_ruby/models/generic_state"
|
require "jam_ruby/models/generic_state"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
class JamRuby::AffiliateLegalese < ActiveRecord::Base
|
||||||
|
self.table_name = 'affiliate_legalese'
|
||||||
|
|
||||||
|
has_many :affiliate_partners, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :legalese_id
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
class JamRuby::AffiliateMonthlyPayment < ActiveRecord::Base
|
||||||
|
|
||||||
|
belongs_to :affiliate_partner, class_name: 'JamRuby::AffiliatePartner', inverse_of: :months
|
||||||
|
|
||||||
|
|
||||||
|
def self.index(user, options)
|
||||||
|
unless user.affiliate_partner
|
||||||
|
return [[], nil]
|
||||||
|
end
|
||||||
|
|
||||||
|
page = options[:page].to_i
|
||||||
|
per_page = options[:per_page].to_i
|
||||||
|
|
||||||
|
if page == 0
|
||||||
|
page = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if per_page == 0
|
||||||
|
per_page = 50
|
||||||
|
end
|
||||||
|
|
||||||
|
start = (page -1 ) * per_page
|
||||||
|
limit = per_page
|
||||||
|
|
||||||
|
query = AffiliateMonthlyPayment
|
||||||
|
.paginate(page: page, per_page: per_page)
|
||||||
|
.where(affiliate_partner_id: user.affiliate_partner.id)
|
||||||
|
.order('year ASC, month ASC')
|
||||||
|
|
||||||
|
if query.length == 0
|
||||||
|
[query, nil]
|
||||||
|
elsif query.length < limit
|
||||||
|
[query, nil]
|
||||||
|
else
|
||||||
|
[query, start + limit]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -1,25 +1,75 @@
|
||||||
class JamRuby::AffiliatePartner < ActiveRecord::Base
|
class JamRuby::AffiliatePartner < ActiveRecord::Base
|
||||||
belongs_to :partner_user, :class_name => "JamRuby::User", :foreign_key => :partner_user_id
|
self.table_name = 'affiliate_partners'
|
||||||
has_many :user_referrals, :class_name => "JamRuby::User", :foreign_key => :affiliate_referral_id
|
|
||||||
|
|
||||||
|
belongs_to :partner_user, :class_name => "JamRuby::User", :foreign_key => :partner_user_id, inverse_of: :affiliate_partner
|
||||||
|
has_many :user_referrals, :class_name => "JamRuby::User", :foreign_key => :affiliate_referral_id
|
||||||
|
belongs_to :affiliate_legalese, :class_name => "JamRuby::AffiliateLegalese", :foreign_key => :legalese_id
|
||||||
|
has_many :sale_line_items, :class_name => 'JamRuby::SaleLineItem', foreign_key: :affiliate_referral_id
|
||||||
|
has_many :quarters, :class_name => 'JamRuby::AffiliateQuarterlyPayment', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
|
||||||
|
has_many :months, :class_name => 'JamRuby::AffiliateMonthlyPayment', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
|
||||||
|
has_many :traffic_totals, :class_name => 'JamRuby::AffiliateTrafficTotal', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
|
||||||
|
has_many :visits, :class_name => 'JamRuby::AffiliateReferralVisit', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
|
||||||
attr_accessible :partner_name, :partner_code, :partner_user_id
|
attr_accessible :partner_name, :partner_code, :partner_user_id
|
||||||
|
|
||||||
|
ENTITY_TYPES = %w{ Individual Sole\ Proprietor Limited\ Liability\ Company\ (LLC) Partnership Trust/Estate S\ Corporation C\ Corporation Other }
|
||||||
|
|
||||||
|
KEY_ADDR1 = 'address1'
|
||||||
|
KEY_ADDR2 = 'address2'
|
||||||
|
KEY_CITY = 'city'
|
||||||
|
KEY_STATE = 'state'
|
||||||
|
KEY_POSTAL = 'postal_code'
|
||||||
|
KEY_COUNTRY = 'country'
|
||||||
|
|
||||||
|
# ten dollars in cents
|
||||||
|
PAY_THRESHOLD = 10 * 100
|
||||||
|
|
||||||
|
AFFILIATE_PARAMS="utm_source=affiliate&utm_medium=affiliate&utm_campaign=2015-affiliate-custom&affiliate="
|
||||||
|
|
||||||
|
ADDRESS_SCHEMA = {
|
||||||
|
KEY_ADDR1 => '',
|
||||||
|
KEY_ADDR2 => '',
|
||||||
|
KEY_CITY => '',
|
||||||
|
KEY_STATE => '',
|
||||||
|
KEY_POSTAL => '',
|
||||||
|
KEY_COUNTRY => '',
|
||||||
|
}
|
||||||
|
|
||||||
PARAM_REFERRAL = :ref
|
PARAM_REFERRAL = :ref
|
||||||
PARAM_COOKIE = :affiliate_ref
|
PARAM_COOKIE = :affiliate_ref
|
||||||
|
|
||||||
PARTNER_CODE_REGEX = /^[#{Regexp.escape('abcdefghijklmnopqrstuvwxyz0123456789-._+,')}]+{2,128}$/i
|
PARTNER_CODE_REGEX = /^[#{Regexp.escape('abcdefghijklmnopqrstuvwxyz0123456789-._+,')}]+{2,128}$/i
|
||||||
|
|
||||||
validates :user_email, format: {with: JamRuby::User::VALID_EMAIL_REGEX}, :if => :user_email
|
#validates :user_email, format: {with: JamRuby::User::VALID_EMAIL_REGEX}, :if => :user_email
|
||||||
validates :partner_name, presence: true
|
#validates :partner_code, format: { with: PARTNER_CODE_REGEX }, :allow_blank => true
|
||||||
validates :partner_code, presence: true, format: { with: PARTNER_CODE_REGEX }
|
validates :entity_type, inclusion: {in: ENTITY_TYPES, message: "invalid entity type"}
|
||||||
validates :partner_user, presence: true
|
|
||||||
|
|
||||||
|
serialize :address, JSON
|
||||||
|
|
||||||
|
before_save do |record|
|
||||||
|
record.address ||= ADDRESS_SCHEMA.clone
|
||||||
|
record.entity_type ||= ENTITY_TYPES.first
|
||||||
|
end
|
||||||
|
|
||||||
|
# used by admin
|
||||||
def self.create_with_params(params={})
|
def self.create_with_params(params={})
|
||||||
|
raise 'not supported'
|
||||||
oo = self.new
|
oo = self.new
|
||||||
oo.partner_name = params[:partner_name].try(:strip)
|
oo.partner_name = params[:partner_name].try(:strip)
|
||||||
oo.partner_code = params[:partner_code].try(:strip).try(:downcase)
|
oo.partner_code = params[:partner_code].try(:strip).try(:downcase)
|
||||||
oo.partner_user = User.where(:email => params[:user_email].try(:strip)).limit(1).first
|
oo.partner_user = User.where(:email => params[:user_email].try(:strip)).limit(1).first
|
||||||
oo.partner_user_id = oo.partner_user.try(:id)
|
oo.partner_user_id = oo.partner_user.try(:id)
|
||||||
|
oo.entity_type = params[:entity_type] || ENTITY_TYPES.first
|
||||||
|
oo.save
|
||||||
|
oo
|
||||||
|
end
|
||||||
|
|
||||||
|
# used by web
|
||||||
|
def self.create_with_web_params(user, params={})
|
||||||
|
oo = self.new
|
||||||
|
oo.partner_name = params[:partner_name].try(:strip)
|
||||||
|
oo.partner_user = user if user # user is not required
|
||||||
|
oo.entity_type = params[:entity_type] || ENTITY_TYPES.first
|
||||||
|
oo.signed_at = Time.now
|
||||||
oo.save
|
oo.save
|
||||||
oo
|
oo
|
||||||
end
|
end
|
||||||
|
|
@ -41,4 +91,382 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
|
||||||
block_given? ? yield(by_date) : by_date
|
block_given? ? yield(by_date) : by_date
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def signed_legalese(legalese)
|
||||||
|
self.affiliate_legalese = legalese
|
||||||
|
self.signed_at = Time.now
|
||||||
|
save!
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_address_value(key, val)
|
||||||
|
self.address[key] = val
|
||||||
|
self.update_attribute(:address, self.address)
|
||||||
|
end
|
||||||
|
|
||||||
|
def address_value(key)
|
||||||
|
self.address[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
def created_within_affiliate_window(user, sale_time)
|
||||||
|
sale_time - user.created_at < 2.years
|
||||||
|
end
|
||||||
|
|
||||||
|
def should_attribute_sale?(shopping_cart)
|
||||||
|
if shopping_cart.is_jam_track?
|
||||||
|
if created_within_affiliate_window(shopping_cart.user, Time.now)
|
||||||
|
product_info = shopping_cart.product_info
|
||||||
|
# subtract the total quantity from the freebie quantity, to see how much we should attribute to them
|
||||||
|
real_quantity = product_info[:quantity].to_i - product_info[:marked_for_redeem].to_i
|
||||||
|
{fee_in_cents: real_quantity * 20}
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise 'shopping cart type not implemented yet'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def cumulative_earnings_in_dollars
|
||||||
|
cumulative_earnings_in_cents.to_f / 100.to_f
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.quarter_info(date)
|
||||||
|
|
||||||
|
year = date.year
|
||||||
|
|
||||||
|
# which quarter?
|
||||||
|
quarter = -1
|
||||||
|
if date.month >= 1 && date.month <= 3
|
||||||
|
quarter = 0
|
||||||
|
elsif date.month >= 4 && date.month <= 6
|
||||||
|
quarter = 1
|
||||||
|
elsif date.month >= 7 && date.month <= 9
|
||||||
|
quarter = 2
|
||||||
|
elsif date.month >= 10 && date.month <= 12
|
||||||
|
quarter = 3
|
||||||
|
end
|
||||||
|
|
||||||
|
raise 'quarter should never be -1' if quarter == -1
|
||||||
|
|
||||||
|
previous_quarter = quarter - 1
|
||||||
|
previous_year = date.year
|
||||||
|
if previous_quarter == -1
|
||||||
|
previous_quarter = 3
|
||||||
|
previous_year = year - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
raise 'previous quarter should never be -1' if previous_quarter == -1
|
||||||
|
|
||||||
|
{year: year, quarter: quarter, previous_quarter: previous_quarter, previous_year: previous_year}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.did_quarter_elapse?(quarter_info, last_tallied_info)
|
||||||
|
if last_tallied_info.nil?
|
||||||
|
true
|
||||||
|
else
|
||||||
|
quarter_info == last_tallied_info
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# meant to be run regularly; this routine will make summarized counts in the
|
||||||
|
# AffiliateQuarterlyPayment table
|
||||||
|
# AffiliatePartner.cumulative_earnings_in_cents, AffiliatePartner.referral_user_count
|
||||||
|
def self.tally_up(day)
|
||||||
|
|
||||||
|
AffiliatePartner.transaction do
|
||||||
|
quarter_info = quarter_info(day)
|
||||||
|
last_tallied_info = quarter_info(GenericState.affiliate_tallied_at) if GenericState.affiliate_tallied_at
|
||||||
|
|
||||||
|
quarter_elapsed = did_quarter_elapse?(quarter_info, last_tallied_info)
|
||||||
|
|
||||||
|
if quarter_elapsed
|
||||||
|
tally_monthly_payments(quarter_info[:previous_year], quarter_info[:previous_quarter])
|
||||||
|
tally_quarterly_payments(quarter_info[:previous_year], quarter_info[:previous_quarter])
|
||||||
|
end
|
||||||
|
tally_monthly_payments(quarter_info[:year], quarter_info[:quarter])
|
||||||
|
tally_quarterly_payments(quarter_info[:year], quarter_info[:quarter])
|
||||||
|
|
||||||
|
tally_traffic_totals(GenericState.affiliate_tallied_at, day)
|
||||||
|
|
||||||
|
tally_partner_totals
|
||||||
|
|
||||||
|
state = GenericState.singleton
|
||||||
|
state.affiliate_tallied_at = day
|
||||||
|
state.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# this just makes sure that the quarter rows exist before later manipulations with UPDATEs
|
||||||
|
def self.ensure_quarters_exist(year, quarter)
|
||||||
|
|
||||||
|
sql = %{
|
||||||
|
INSERT INTO affiliate_quarterly_payments (quarter, year, affiliate_partner_id)
|
||||||
|
(SELECT #{quarter}, #{year}, affiliate_partners.id FROM affiliate_partners WHERE affiliate_partners.partner_user_id IS NOT NULL AND affiliate_partners.id NOT IN
|
||||||
|
(SELECT affiliate_partner_id FROM affiliate_quarterly_payments WHERE year = #{year} AND quarter = #{quarter}))
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
end
|
||||||
|
|
||||||
|
# this just makes sure that the quarter rows exist before later manipulations with UPDATEs
|
||||||
|
def self.ensure_months_exist(year, quarter)
|
||||||
|
|
||||||
|
months = [1, 2, 3].collect! { |i| quarter * 3 + i }
|
||||||
|
|
||||||
|
months.each do |month|
|
||||||
|
sql = %{
|
||||||
|
INSERT INTO affiliate_monthly_payments (month, year, affiliate_partner_id)
|
||||||
|
(SELECT #{month}, #{year}, affiliate_partners.id FROM affiliate_partners WHERE affiliate_partners.partner_user_id IS NOT NULL AND affiliate_partners.id NOT IN
|
||||||
|
(SELECT affiliate_partner_id FROM affiliate_monthly_payments WHERE year = #{year} AND month = #{month}))
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def self.sale_items_subquery(start_date, end_date, table_name)
|
||||||
|
%{
|
||||||
|
FROM sale_line_items
|
||||||
|
WHERE
|
||||||
|
(DATE(sale_line_items.created_at) >= DATE('#{start_date}') AND DATE(sale_line_items.created_at) <= DATE('#{end_date}'))
|
||||||
|
AND
|
||||||
|
sale_line_items.affiliate_referral_id = #{table_name}.affiliate_partner_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.sale_items_refunded_subquery(start_date, end_date, table_name)
|
||||||
|
%{
|
||||||
|
FROM sale_line_items
|
||||||
|
WHERE
|
||||||
|
(DATE(sale_line_items.affiliate_refunded_at) >= DATE('#{start_date}') AND DATE(sale_line_items.affiliate_refunded_at) <= DATE('#{end_date}'))
|
||||||
|
AND
|
||||||
|
sale_line_items.affiliate_referral_id = #{table_name}.affiliate_partner_id
|
||||||
|
AND
|
||||||
|
sale_line_items.affiliate_refunded = TRUE
|
||||||
|
}
|
||||||
|
end
|
||||||
|
# total up quarters by looking in sale_line_items for items that are marked as having a affiliate_referral_id
|
||||||
|
# don't forget to substract any sale_line_items that have a affiliate_refunded = TRUE
|
||||||
|
def self.total_months(year, quarter)
|
||||||
|
|
||||||
|
months = [1, 2, 3].collect! { |i| quarter * 3 + i }
|
||||||
|
|
||||||
|
|
||||||
|
months.each do |month|
|
||||||
|
|
||||||
|
start_date, end_date = boundary_dates_for_month(year, month)
|
||||||
|
|
||||||
|
sql = %{
|
||||||
|
UPDATE affiliate_monthly_payments
|
||||||
|
SET
|
||||||
|
last_updated = NOW(),
|
||||||
|
jamtracks_sold =
|
||||||
|
COALESCE(
|
||||||
|
(SELECT COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND sale_line_items.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
|
||||||
|
#{sale_items_subquery(start_date, end_date, 'affiliate_monthly_payments')}
|
||||||
|
), 0)
|
||||||
|
+
|
||||||
|
COALESCE(
|
||||||
|
(SELECT -COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND sale_line_items.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
|
||||||
|
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_monthly_payments')}
|
||||||
|
), 0),
|
||||||
|
due_amount_in_cents =
|
||||||
|
COALESCE(
|
||||||
|
(SELECT SUM(affiliate_referral_fee_in_cents)
|
||||||
|
#{sale_items_subquery(start_date, end_date, 'affiliate_monthly_payments')}
|
||||||
|
), 0)
|
||||||
|
+
|
||||||
|
COALESCE(
|
||||||
|
(SELECT -SUM(affiliate_referral_fee_in_cents)
|
||||||
|
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_monthly_payments')}
|
||||||
|
), 0)
|
||||||
|
|
||||||
|
WHERE closed = FALSE AND year = #{year} AND month = #{month}
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# close any quarters that are done, so we don't manipulate them again
|
||||||
|
def self.close_months(year, quarter)
|
||||||
|
# close any quarters that occurred before this quarter
|
||||||
|
month = quarter * 3 + 1
|
||||||
|
|
||||||
|
sql = %{
|
||||||
|
UPDATE affiliate_monthly_payments
|
||||||
|
SET
|
||||||
|
closed = TRUE, closed_at = NOW()
|
||||||
|
WHERE year < #{year} OR month < #{month}
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# total up quarters by looking in sale_line_items for items that are marked as having a affiliate_referral_id
|
||||||
|
# don't forget to substract any sale_line_items that have a affiliate_refunded = TRUE
|
||||||
|
def self.total_quarters(year, quarter)
|
||||||
|
start_date, end_date = boundary_dates(year, quarter)
|
||||||
|
|
||||||
|
sql = %{
|
||||||
|
UPDATE affiliate_quarterly_payments
|
||||||
|
SET
|
||||||
|
last_updated = NOW(),
|
||||||
|
jamtracks_sold =
|
||||||
|
COALESCE(
|
||||||
|
(SELECT COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND sale_line_items.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
|
||||||
|
#{sale_items_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
|
||||||
|
), 0)
|
||||||
|
+
|
||||||
|
COALESCE(
|
||||||
|
(SELECT -COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND sale_line_items.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
|
||||||
|
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
|
||||||
|
), 0),
|
||||||
|
due_amount_in_cents =
|
||||||
|
COALESCE(
|
||||||
|
(SELECT SUM(affiliate_referral_fee_in_cents)
|
||||||
|
#{sale_items_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
|
||||||
|
), 0)
|
||||||
|
+
|
||||||
|
COALESCE(
|
||||||
|
(SELECT -SUM(affiliate_referral_fee_in_cents)
|
||||||
|
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
|
||||||
|
), 0)
|
||||||
|
|
||||||
|
WHERE closed = FALSE AND paid = FALSE AND year = #{year} AND quarter = #{quarter}
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
end
|
||||||
|
|
||||||
|
# close any quarters that are done, so we don't manipulate them again
|
||||||
|
def self.close_quarters(year, quarter)
|
||||||
|
# close any quarters that occurred before this quarter
|
||||||
|
sql = %{
|
||||||
|
UPDATE affiliate_quarterly_payments
|
||||||
|
SET
|
||||||
|
closed = TRUE, closed_at = NOW()
|
||||||
|
WHERE year < #{year} OR quarter < #{quarter}
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.tally_quarterly_payments(year, quarter)
|
||||||
|
ensure_quarters_exist(year, quarter)
|
||||||
|
|
||||||
|
total_quarters(year, quarter)
|
||||||
|
|
||||||
|
close_quarters(year, quarter)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def self.tally_monthly_payments(year, quarter)
|
||||||
|
ensure_months_exist(year, quarter)
|
||||||
|
|
||||||
|
total_months(year, quarter)
|
||||||
|
|
||||||
|
close_months(year, quarter)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.tally_partner_totals
|
||||||
|
sql = %{
|
||||||
|
UPDATE affiliate_partners SET
|
||||||
|
referral_user_count = (SELECT count(*) FROM users WHERE affiliate_partners.id = users.affiliate_referral_id),
|
||||||
|
cumulative_earnings_in_cents = (SELECT COALESCE(SUM(due_amount_in_cents), 0) FROM affiliate_quarterly_payments AS aqp WHERE aqp.affiliate_partner_id = affiliate_partners.id AND closed = TRUE and paid = TRUE)
|
||||||
|
}
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.tally_traffic_totals(last_tallied_at, target_day)
|
||||||
|
|
||||||
|
if last_tallied_at
|
||||||
|
start_date = last_tallied_at.to_date
|
||||||
|
end_date = target_day.to_date
|
||||||
|
else
|
||||||
|
start_date = target_day.to_date - 1
|
||||||
|
end_date = target_day.to_date
|
||||||
|
end
|
||||||
|
|
||||||
|
if start_date == end_date
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
sql = %{
|
||||||
|
INSERT INTO affiliate_traffic_totals(SELECT day, 0, 0, ap.id FROM affiliate_partners AS ap CROSS JOIN (select (generate_series('#{start_date}', '#{end_date - 1}', '1 day'::interval))::date as day) AS lurp)
|
||||||
|
}
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
|
||||||
|
sql = %{
|
||||||
|
UPDATE affiliate_traffic_totals traffic SET visits = COALESCE((SELECT COALESCE(count(affiliate_partner_id), 0) FROM affiliate_referral_visits v WHERE DATE(v.created_at) >= DATE('#{start_date}') AND DATE(v.created_at) < DATE('#{end_date}') AND v.created_at::date = traffic.day AND v.affiliate_partner_id = traffic.affiliate_partner_id GROUP BY affiliate_partner_id, v.created_at::date ), 0) WHERE traffic.day >= DATE('#{start_date}') AND traffic.day < DATE('#{end_date}')
|
||||||
|
}
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
|
||||||
|
sql = %{
|
||||||
|
UPDATE affiliate_traffic_totals traffic SET signups = COALESCE((SELECT COALESCE(count(v.id), 0) FROM users v WHERE DATE(v.created_at) >= DATE('#{start_date}') AND DATE(v.created_at) < DATE('#{end_date}') AND v.created_at::date = traffic.day AND v.affiliate_referral_id = traffic.affiliate_partner_id GROUP BY affiliate_referral_id, v.created_at::date ), 0) WHERE traffic.day >= DATE('#{start_date}') AND traffic.day < DATE('#{end_date}')
|
||||||
|
}
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.boundary_dates(year, quarter)
|
||||||
|
if quarter == 0
|
||||||
|
[Date.new(year, 1, 1), Date.new(year, 3, 31)]
|
||||||
|
elsif quarter == 1
|
||||||
|
[Date.new(year, 4, 1), Date.new(year, 6, 30)]
|
||||||
|
elsif quarter == 2
|
||||||
|
[Date.new(year, 7, 1), Date.new(year, 9, 30)]
|
||||||
|
elsif quarter == 3
|
||||||
|
[Date.new(year, 10, 1), Date.new(year, 12, 31)]
|
||||||
|
else
|
||||||
|
raise "invalid quarter #{quarter}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# 1-based month
|
||||||
|
def self.boundary_dates_for_month(year, month)
|
||||||
|
[Date.new(year, month, 1), Date.civil(year, month, -1)]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Finds all affiliates that need to be paid
|
||||||
|
def self.unpaid
|
||||||
|
|
||||||
|
joins(:quarters)
|
||||||
|
.where('affiliate_quarterly_payments.paid = false').where('affiliate_quarterly_payments.closed = true')
|
||||||
|
.group('affiliate_partners.id')
|
||||||
|
.having('sum(due_amount_in_cents) >= ?', PAY_THRESHOLD)
|
||||||
|
.order('sum(due_amount_in_cents) DESC')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# does this one affiliate need to be paid?
|
||||||
|
def unpaid
|
||||||
|
due_amount_in_cents > PAY_THRESHOLD
|
||||||
|
end
|
||||||
|
|
||||||
|
# admin function: mark the affiliate paid
|
||||||
|
def mark_paid
|
||||||
|
if unpaid
|
||||||
|
transaction do
|
||||||
|
now = Time.now
|
||||||
|
quarters.where(paid:false, closed:true).update_all(paid:true, paid_at: now)
|
||||||
|
self.last_paid_at = now
|
||||||
|
self.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# how much is this affiliate due?
|
||||||
|
def due_amount_in_cents
|
||||||
|
total_in_cents = 0
|
||||||
|
quarters.where(paid:false, closed:true).each do |quarter|
|
||||||
|
total_in_cents = total_in_cents + quarter.due_amount_in_cents
|
||||||
|
end
|
||||||
|
total_in_cents
|
||||||
|
end
|
||||||
|
|
||||||
|
def affiliate_query_params
|
||||||
|
AffiliatePartner::AFFILIATE_PARAMS + self.id.to_s
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
module JamRuby
|
||||||
|
class AffiliatePayment < ActiveRecord::Base
|
||||||
|
|
||||||
|
belongs_to :affiliate_monthly_payment
|
||||||
|
belongs_to :affiliate_quarterly_payment
|
||||||
|
|
||||||
|
def self.index(user, options)
|
||||||
|
|
||||||
|
unless user.affiliate_partner
|
||||||
|
return [[], nil]
|
||||||
|
end
|
||||||
|
|
||||||
|
affiliate_partner_id = user.affiliate_partner.id
|
||||||
|
|
||||||
|
|
||||||
|
page = options[:page].to_i
|
||||||
|
per_page = options[:per_page].to_i
|
||||||
|
|
||||||
|
if page == 0
|
||||||
|
page = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if per_page == 0
|
||||||
|
per_page = 50
|
||||||
|
end
|
||||||
|
|
||||||
|
start = (page -1 ) * per_page
|
||||||
|
limit = per_page
|
||||||
|
|
||||||
|
|
||||||
|
query = AffiliatePayment
|
||||||
|
.includes(affiliate_quarterly_payment: [], affiliate_monthly_payment:[])
|
||||||
|
.where(affiliate_partner_id: affiliate_partner_id)
|
||||||
|
.where("(payment_type='quarterly' AND closed = true) OR payment_type='monthly'")
|
||||||
|
.where('(paid = TRUE or due_amount_in_cents < 10000 or paid is NULL)')
|
||||||
|
.paginate(:page => page, :per_page => limit)
|
||||||
|
.order('year ASC, time_sort ASC, payment_type ASC')
|
||||||
|
|
||||||
|
if query.length == 0
|
||||||
|
[query, nil]
|
||||||
|
elsif query.length < limit
|
||||||
|
[query, nil]
|
||||||
|
else
|
||||||
|
[query, start + limit]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
class JamRuby::AffiliateQuarterlyPayment < ActiveRecord::Base
|
||||||
|
|
||||||
|
belongs_to :affiliate_partner, class_name: 'JamRuby::AffiliatePartner', inverse_of: :quarters
|
||||||
|
|
||||||
|
|
||||||
|
def self.index(user, options)
|
||||||
|
unless user.affiliate_partner
|
||||||
|
return [[], nil]
|
||||||
|
end
|
||||||
|
|
||||||
|
page = options[:page].to_i
|
||||||
|
per_page = options[:per_page].to_i
|
||||||
|
|
||||||
|
if page == 0
|
||||||
|
page = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if per_page == 0
|
||||||
|
per_page = 50
|
||||||
|
end
|
||||||
|
|
||||||
|
start = (page -1 ) * per_page
|
||||||
|
limit = per_page
|
||||||
|
|
||||||
|
query = AffiliateQuarterlyPayment
|
||||||
|
.paginate(page: page, per_page: per_page)
|
||||||
|
.where(affiliate_partner_id: user.affiliate_partner.id)
|
||||||
|
.where(closed:true)
|
||||||
|
.where(paid:true)
|
||||||
|
.order('year ASC, quarter ASC')
|
||||||
|
|
||||||
|
if query.length == 0
|
||||||
|
[query, nil]
|
||||||
|
elsif query.length < limit
|
||||||
|
[query, nil]
|
||||||
|
else
|
||||||
|
[query, start + limit]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
class JamRuby::AffiliateReferralVisit < ActiveRecord::Base
|
||||||
|
|
||||||
|
belongs_to :affiliate_partner, class_name: 'JamRuby::AffiliatePartner', inverse_of: :visits
|
||||||
|
|
||||||
|
validates :affiliate_partner_id, numericality: {only_integer: true}, :allow_nil => true
|
||||||
|
validates :visited_url, length: {maximum: 1000}
|
||||||
|
validates :referral_url, length: {maximum: 1000}
|
||||||
|
validates :ip_address, presence: true, length: {maximum:1000}
|
||||||
|
validates :first_visit, inclusion: {in: [true, false]}
|
||||||
|
validates :user_id, length: {maximum:64}
|
||||||
|
|
||||||
|
def self.track(options = {})
|
||||||
|
visit = AffiliateReferralVisit.new
|
||||||
|
visit.affiliate_partner_id = options[:affiliate_id]
|
||||||
|
visit.ip_address = options[:remote_ip]
|
||||||
|
visit.visited_url = options[:visited_url]
|
||||||
|
visit.referral_url = options[:referral_url]
|
||||||
|
visit.first_visit = options[:visited].nil?
|
||||||
|
visit.user_id = options[:current_user].id if options[:current_user]
|
||||||
|
visit.save
|
||||||
|
visit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
class JamRuby::AffiliateTrafficTotal < ActiveRecord::Base
|
||||||
|
|
||||||
|
belongs_to :affiliate_partner, class_name: 'JamRuby::AffiliatePartner', inverse_of: :traffic_totals
|
||||||
|
|
||||||
|
|
||||||
|
def self.index(user, options)
|
||||||
|
unless user.affiliate_partner
|
||||||
|
return [[], nil]
|
||||||
|
end
|
||||||
|
|
||||||
|
page = options[:page].to_i
|
||||||
|
per_page = options[:per_page].to_i
|
||||||
|
|
||||||
|
if page == 0
|
||||||
|
page = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if per_page == 0
|
||||||
|
per_page = 50
|
||||||
|
end
|
||||||
|
|
||||||
|
start = (page -1 ) * per_page
|
||||||
|
limit = per_page
|
||||||
|
|
||||||
|
query = AffiliateTrafficTotal
|
||||||
|
.paginate(page: page, per_page: per_page)
|
||||||
|
.where(affiliate_partner_id: user.affiliate_partner.id)
|
||||||
|
.where('visits != 0 OR signups != 0')
|
||||||
|
.order('day ASC')
|
||||||
|
|
||||||
|
if query.length == 0
|
||||||
|
[query, nil]
|
||||||
|
elsif query.length < limit
|
||||||
|
[query, nil]
|
||||||
|
else
|
||||||
|
[query, start + limit]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -28,7 +28,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
def signup_hint
|
def signup_hint
|
||||||
SignupHint.find_by_anonymous_user_id(@id)
|
SignupHint.where(anonymous_user_id: @id).where('expires_at > ?', Time.now).first
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
module JamRuby
|
||||||
|
class FingerprintWhitelist < ActiveRecord::Base
|
||||||
|
|
||||||
|
@@log = Logging.logger[FingerprintWhitelist]
|
||||||
|
|
||||||
|
validates :fingerprint, presence: true, uniqueness: true
|
||||||
|
has_many :machine_fingerprint, class_name: 'JamRuby::MachineFingerprint', foreign_key: :fingerprint
|
||||||
|
|
||||||
|
def admin_url
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/fingerprint_whitelists/" + id
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{fingerprint}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
module JamRuby
|
||||||
|
class FraudAlert < ActiveRecord::Base
|
||||||
|
|
||||||
|
@@log = Logging.logger[MachineExtra]
|
||||||
|
|
||||||
|
belongs_to :machine_fingerprint, :class_name => "JamRuby::MachineFingerprint"
|
||||||
|
belongs_to :user, :class_name => "JamRuby::User"
|
||||||
|
|
||||||
|
|
||||||
|
def self.create(machine_fingerprint, user)
|
||||||
|
fraud = FraudAlert.new
|
||||||
|
fraud.machine_fingerprint = machine_fingerprint
|
||||||
|
fraud.user = user
|
||||||
|
fraud.save
|
||||||
|
|
||||||
|
unless fraud.save
|
||||||
|
@@log.error("unable to create fraud: #{fraud.errors.inspect}")
|
||||||
|
end
|
||||||
|
fraud
|
||||||
|
end
|
||||||
|
|
||||||
|
def admin_url
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/fraud_alerts/" + id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -23,9 +23,14 @@ module JamRuby
|
||||||
(database_environment == 'development' && Environment.mode == 'development')
|
(database_environment == 'development' && Environment.mode == 'development')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.affiliate_tallied_at
|
||||||
|
GenericState.singleton.affiliate_tallied_at
|
||||||
|
end
|
||||||
|
|
||||||
def self.singleton
|
def self.singleton
|
||||||
GenericState.find('default')
|
GenericState.find('default')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -263,5 +263,10 @@ module JamRuby
|
||||||
jam_track_rights.where("user_id=?", user).first
|
jam_track_rights.where("user_id=?", user).first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def short_plan_code
|
||||||
|
prefix = 'jamtrack-'
|
||||||
|
plan_code[prefix.length..-1]
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -229,8 +229,8 @@ module JamRuby
|
||||||
return "no fingerprint specified"
|
return "no fingerprint specified"
|
||||||
end
|
end
|
||||||
|
|
||||||
all_fingerprint = fingerprint[:all]
|
all_fingerprint = fingerprint.delete(:all)
|
||||||
running_fingerprint = fingerprint[:running]
|
running_fingerprint = fingerprint.delete(:running)
|
||||||
|
|
||||||
if all_fingerprint.blank?
|
if all_fingerprint.blank?
|
||||||
return "no all fingerprint specified"
|
return "no all fingerprint specified"
|
||||||
|
|
@ -240,6 +240,9 @@ module JamRuby
|
||||||
return "no running fingerprint specified"
|
return "no running fingerprint specified"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
all_fingerprint_extra = fingerprint[all_fingerprint]
|
||||||
|
running_fingerprint_extra = fingerprint[running_fingerprint]
|
||||||
|
|
||||||
if redeemed && !redeemed_and_fingerprinted
|
if redeemed && !redeemed_and_fingerprinted
|
||||||
# if this is a free JamTrack, we need to check for fraud or accidental misuse
|
# if this is a free JamTrack, we need to check for fraud or accidental misuse
|
||||||
|
|
||||||
|
|
@ -250,41 +253,80 @@ module JamRuby
|
||||||
return "already redeemed another"
|
return "already redeemed another"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if FingerprintWhitelist.select('id').find_by_fingerprint(all_fingerprint)
|
||||||
|
# we can short circuit out of the rest of the check, since this is a known bad fingerprint
|
||||||
|
@@log.debug("ignoring 'all' hash found in whitelist")
|
||||||
|
else
|
||||||
# can we find a jam track that belongs to someone else with the same fingerprint
|
# can we find a jam track that belongs to someone else with the same fingerprint
|
||||||
match = MachineFingerprint.find_by_fingerprint(all_fingerprint)
|
conflict = MachineFingerprint.select('count(id) as count').where('user_id != ?', current_user.id).where(fingerprint: all_fingerprint).where(remote_ip: remote_ip).where('created_at > ?', APP_CONFIG.expire_fingerprint_days.days.ago).first
|
||||||
|
conflict_count = conflict['count'].to_i
|
||||||
|
|
||||||
|
if conflict_count >= APP_CONFIG.found_conflict_count
|
||||||
|
mf = MachineFingerprint.create(all_fingerprint, current_user, MachineFingerprint::TAKEN_ON_FRAUD_CONFLICT, MachineFingerprint::PRINT_TYPE_ACTIVE, remote_ip, all_fingerprint_extra, self)
|
||||||
|
|
||||||
|
# record the alert
|
||||||
|
fraud = FraudAlert.create(mf, current_user) if mf.valid?
|
||||||
|
fraud_admin_url = fraud.admin_url if fraud
|
||||||
|
|
||||||
|
|
||||||
if match && match.user != current_user
|
|
||||||
AdminMailer.alerts(subject: "'All' fingerprint collision by #{current_user.name}",
|
AdminMailer.alerts(subject: "'All' fingerprint collision by #{current_user.name}",
|
||||||
body: "MachineFingerprint #{match.inspect}\n\nCurrent User: #{current_user.admin_url}").deliver
|
body: "Current User: #{current_user.admin_url}\n\n Fraud Alert: #{fraud_admin_url}").deliver
|
||||||
|
|
||||||
# try to record the other fingerprint
|
# try to record the other fingerprint
|
||||||
MachineFingerprint.create(running_fingerprint, current_user, MachineFingerprint::TAKEN_ON_FRAUD_CONFLICT, MachineFingerprint::PRINT_TYPE_ACTIVE, remote_ip, self)
|
mf = MachineFingerprint.create(running_fingerprint, current_user, MachineFingerprint::TAKEN_ON_FRAUD_CONFLICT, MachineFingerprint::PRINT_TYPE_ACTIVE, remote_ip, running_fingerprint_extra, self)
|
||||||
|
|
||||||
if APP_CONFIG.error_on_fraud
|
if APP_CONFIG.error_on_fraud
|
||||||
return "other user has 'all' fingerprint"
|
return "other user has 'all' fingerprint"
|
||||||
|
else
|
||||||
|
self.redeemed_and_fingerprinted = true
|
||||||
|
save!
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
if all_fingerprint != running_fingerprint
|
if all_fingerprint != running_fingerprint
|
||||||
match = MachineFingerprint.find_by_fingerprint(running_fingerprint)
|
if FingerprintWhitelist.select('id').find_by_fingerprint(running_fingerprint)
|
||||||
|
# we can short circuit out of the rest of the check, since this is a known bad fingerprint
|
||||||
|
@@log.debug("ignoring 'running' hash found in whitelist")
|
||||||
|
else
|
||||||
|
|
||||||
if match && match.user != current_user
|
conflict = MachineFingerprint.select('count(id) as count').where('user_id != ?', current_user.id).where(fingerprint: running_fingerprint).where(remote_ip: remote_ip).where('created_at > ?', APP_CONFIG.expire_fingerprint_days.days.ago).first
|
||||||
|
conflict_count = conflict['count'].to_i
|
||||||
|
if conflict_count >= APP_CONFIG.found_conflict_count
|
||||||
|
mf = MachineFingerprint.create(running_fingerprint, current_user, MachineFingerprint::TAKEN_ON_FRAUD_CONFLICT, MachineFingerprint::PRINT_TYPE_ACTIVE, remote_ip, running_fingerprint_extra, self)
|
||||||
|
|
||||||
|
# record the alert
|
||||||
|
fraud = FraudAlert.create(mf, current_user) if mf.valid?
|
||||||
|
fraud_admin_url = fraud.admin_url if fraud
|
||||||
AdminMailer.alerts(subject: "'Running' fingerprint collision by #{current_user.name}",
|
AdminMailer.alerts(subject: "'Running' fingerprint collision by #{current_user.name}",
|
||||||
body: "MachineFingerprint #{match.inspect}\n\nCurrent User: #{current_user.admin_url}").deliver
|
body: "Current User: #{current_user.admin_url}\n\nFraud Alert: #{fraud_admin_url}").deliver\
|
||||||
|
|
||||||
# try to record the other fingerprint
|
# try to record the other fingerprint
|
||||||
MachineFingerprint.create(all_fingerprint, current_user, MachineFingerprint::TAKEN_ON_FRAUD_CONFLICT, MachineFingerprint::PRINT_TYPE_ALL, remote_ip, self)
|
mf = MachineFingerprint.create(all_fingerprint, current_user, MachineFingerprint::TAKEN_ON_FRAUD_CONFLICT, MachineFingerprint::PRINT_TYPE_ALL, remote_ip, all_fingerprint_extra, self)
|
||||||
|
|
||||||
|
|
||||||
if APP_CONFIG.error_on_fraud
|
if APP_CONFIG.error_on_fraud
|
||||||
return "other user has 'running' fingerprint"
|
return "other user has 'running' fingerprint"
|
||||||
|
else
|
||||||
|
self.redeemed_and_fingerprinted = true
|
||||||
|
save!
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
# we made it past all checks; let's slap on the redeemed_fingerprint
|
# we made it past all checks; let's slap on the redeemed_fingerprint
|
||||||
self.redeemed_and_fingerprinted = true
|
self.redeemed_and_fingerprinted = true
|
||||||
|
|
||||||
|
MachineFingerprint.create(all_fingerprint, current_user, MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD, MachineFingerprint::PRINT_TYPE_ALL, remote_ip, all_fingerprint_extra, self)
|
||||||
MachineFingerprint.create(all_fingerprint, current_user, MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD, MachineFingerprint::PRINT_TYPE_ALL, remote_ip, self)
|
|
||||||
if all_fingerprint != running_fingerprint
|
if all_fingerprint != running_fingerprint
|
||||||
MachineFingerprint.create(running_fingerprint, current_user, MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD, MachineFingerprint::PRINT_TYPE_ACTIVE, remote_ip, self)
|
MachineFingerprint.create(running_fingerprint, current_user, MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD, MachineFingerprint::PRINT_TYPE_ACTIVE, remote_ip, running_fingerprint_extra, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
save!
|
save!
|
||||||
|
|
@ -297,11 +339,12 @@ module JamRuby
|
||||||
def self.stats
|
def self.stats
|
||||||
stats = {}
|
stats = {}
|
||||||
|
|
||||||
result = JamTrackRight.select('count(id) as total, count(CASE WHEN signing_44 THEN 1 ELSE NULL END) + count(CASE WHEN signing_48 THEN 1 ELSE NULL END) as signing_count, count(CASE WHEN redeemed THEN 1 ELSE NULL END) as redeem_count').where(is_test_purchase: false).first
|
result = JamTrackRight.select('count(id) as total, count(CASE WHEN signing_44 THEN 1 ELSE NULL END) + count(CASE WHEN signing_48 THEN 1 ELSE NULL END) as signing_count, count(CASE WHEN redeemed THEN 1 ELSE NULL END) as redeem_count, count(last_downloaded_at) as redeemed_and_dl_count').where(is_test_purchase: false).first
|
||||||
|
|
||||||
stats['count'] = result['total'].to_i
|
stats['count'] = result['total'].to_i
|
||||||
stats['signing_count'] = result['signing_count'].to_i
|
stats['signing_count'] = result['signing_count'].to_i
|
||||||
stats['redeemed_count'] = result['redeem_count'].to_i
|
stats['redeemed_count'] = result['redeem_count'].to_i
|
||||||
|
stats['redeemed_and_dl_count'] = result['redeemed_and_dl_count'].to_i
|
||||||
stats['purchased_count'] = stats['count'] - stats['redeemed_count']
|
stats['purchased_count'] = stats['count'] - stats['redeemed_count']
|
||||||
stats
|
stats
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
module JamRuby
|
||||||
|
class MachineExtra < ActiveRecord::Base
|
||||||
|
|
||||||
|
@@log = Logging.logger[MachineExtra]
|
||||||
|
|
||||||
|
belongs_to :machine_fingerprint, :class_name => "JamRuby::MachineFingerprint"
|
||||||
|
|
||||||
|
def self.create(machine_fingerprint, data)
|
||||||
|
me = MachineExtra.new
|
||||||
|
me.machine_fingerprint = machine_fingerprint
|
||||||
|
me.mac_address = data[:mac]
|
||||||
|
me.mac_name = data[:name]
|
||||||
|
me.upstate = data[:upstate]
|
||||||
|
me.ipaddr_0 = data[:ipaddr_0]
|
||||||
|
me.ipaddr_1 = data[:ipaddr_1]
|
||||||
|
me.ipaddr_2 = data[:ipaddr_2]
|
||||||
|
me.ipaddr_3 = data[:ipaddr_3]
|
||||||
|
me.ipaddr_4 = data[:ipaddr_4]
|
||||||
|
me.ipaddr_5 = data[:ipaddr_5]
|
||||||
|
me.save
|
||||||
|
|
||||||
|
unless me.save
|
||||||
|
@@log.error("unable to create machine extra: #{me.errors.inspect}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def admin_url
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/machine_extras/" + id
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{mac_address} #{mac_name} #{upstate ? 'UP' : 'DOWN'} #{ipaddr_0} #{ipaddr_1} #{ipaddr_2} #{ipaddr_3} #{ipaddr_4} #{ipaddr_5}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -5,6 +5,8 @@ module JamRuby
|
||||||
|
|
||||||
belongs_to :user, :class_name => "JamRuby::User"
|
belongs_to :user, :class_name => "JamRuby::User"
|
||||||
belongs_to :jam_track_right, :class_name => "JamRuby::JamTrackRight"
|
belongs_to :jam_track_right, :class_name => "JamRuby::JamTrackRight"
|
||||||
|
has_one :detail, :class_name => "JamRuby::MachineExtra"
|
||||||
|
belongs_to :fingerprint_whitelist, class_name: 'JamRuby::FingerprintWhitelist', foreign_key: :fingerprint
|
||||||
|
|
||||||
TAKEN_ON_SUCCESSFUL_DOWNLOAD = 'dl'
|
TAKEN_ON_SUCCESSFUL_DOWNLOAD = 'dl'
|
||||||
TAKEN_ON_FRAUD_CONFLICT = 'fc'
|
TAKEN_ON_FRAUD_CONFLICT = 'fc'
|
||||||
|
|
@ -15,11 +17,11 @@ module JamRuby
|
||||||
|
|
||||||
validates :user, presence:true
|
validates :user, presence:true
|
||||||
validates :when_taken, :inclusion => {:in => [TAKEN_ON_SUCCESSFUL_DOWNLOAD, TAKEN_ON_FRAUD_CONFLICT]}
|
validates :when_taken, :inclusion => {:in => [TAKEN_ON_SUCCESSFUL_DOWNLOAD, TAKEN_ON_FRAUD_CONFLICT]}
|
||||||
validates :fingerprint, presence: true, uniqueness:true
|
validates :fingerprint, presence: true
|
||||||
validates :print_type, presence: true, :inclusion => {:in =>[PRINT_TYPE_ALL, PRINT_TYPE_ACTIVE]}
|
validates :print_type, presence: true, :inclusion => {:in =>[PRINT_TYPE_ALL, PRINT_TYPE_ACTIVE]}
|
||||||
validates :remote_ip, presence: true
|
validates :remote_ip, presence: true
|
||||||
|
|
||||||
def self.create(fingerprint, user, when_taken, print_type, remote_ip, jam_track_right = nil)
|
def self.create(fingerprint, user, when_taken, print_type, remote_ip, extra, jam_track_right = nil)
|
||||||
mf = MachineFingerprint.new
|
mf = MachineFingerprint.new
|
||||||
mf.fingerprint = fingerprint
|
mf.fingerprint = fingerprint
|
||||||
mf.user = user
|
mf.user = user
|
||||||
|
|
@ -27,9 +29,21 @@ module JamRuby
|
||||||
mf.print_type = print_type
|
mf.print_type = print_type
|
||||||
mf.remote_ip = remote_ip
|
mf.remote_ip = remote_ip
|
||||||
mf.jam_track_right = jam_track_right
|
mf.jam_track_right = jam_track_right
|
||||||
unless mf.save
|
if mf.save
|
||||||
|
MachineExtra.create(mf, extra) if extra
|
||||||
|
else
|
||||||
|
|
||||||
@@log.error("unable to create machine fingerprint: #{mf.errors.inspect}")
|
@@log.error("unable to create machine fingerprint: #{mf.errors.inspect}")
|
||||||
end
|
end
|
||||||
|
mf
|
||||||
|
end
|
||||||
|
|
||||||
|
def admin_url
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/machine_fingerprints/" + id
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{fingerprint} #{remote_ip} #{user} #{detail}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -282,7 +282,7 @@ module JamRuby
|
||||||
return query
|
return query
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.scheduled user
|
def self.scheduled user, only_public = false
|
||||||
# keep unstarted sessions around for 12 hours after scheduled_start
|
# keep unstarted sessions around for 12 hours after scheduled_start
|
||||||
session_not_started = "(music_sessions.scheduled_start > NOW() - '12 hour'::INTERVAL AND music_sessions.started_at IS NULL)"
|
session_not_started = "(music_sessions.scheduled_start > NOW() - '12 hour'::INTERVAL AND music_sessions.started_at IS NULL)"
|
||||||
|
|
||||||
|
|
@ -293,6 +293,7 @@ module JamRuby
|
||||||
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.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.user_id = '#{user.id}'")
|
query = query.where("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}'")
|
||||||
|
|
|
||||||
|
|
@ -100,9 +100,14 @@ module JamRuby
|
||||||
if sale && sale.is_jam_track_sale?
|
if sale && sale.is_jam_track_sale?
|
||||||
if sale.sale_line_items.length == 1
|
if sale.sale_line_items.length == 1
|
||||||
if sale.recurly_total_in_cents == transaction.amount_in_cents
|
if sale.recurly_total_in_cents == transaction.amount_in_cents
|
||||||
jam_track = sale.sale_line_items[0].product
|
line_item = sale.sale_line_items[0]
|
||||||
|
jam_track = line_item.product
|
||||||
jam_track_right = jam_track.right_for_user(transaction.user) if jam_track
|
jam_track_right = jam_track.right_for_user(transaction.user) if jam_track
|
||||||
if jam_track_right
|
if jam_track_right
|
||||||
|
line_item.affiliate_refunded = true
|
||||||
|
line_item.affiliate_refunded_at = Time.now
|
||||||
|
line_item.save!
|
||||||
|
|
||||||
jam_track_right.destroy
|
jam_track_right.destroy
|
||||||
|
|
||||||
# associate which JamTrack we assume this is related to in this one success case
|
# associate which JamTrack we assume this is related to in this one success case
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ module JamRuby
|
||||||
belongs_to :sale, class_name: 'JamRuby::Sale'
|
belongs_to :sale, class_name: 'JamRuby::Sale'
|
||||||
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
|
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
|
||||||
belongs_to :jam_track_right, class_name: 'JamRuby::JamTrackRight'
|
belongs_to :jam_track_right, class_name: 'JamRuby::JamTrackRight'
|
||||||
|
belongs_to :affiliate_referral, class_name: 'JamRuby::AffiliatePartner', foreign_key: :affiliate_referral_id
|
||||||
has_many :recurly_transactions, class_name: 'JamRuby::RecurlyTransactionWebHook', inverse_of: :sale_line_item, foreign_key: 'subscription_id', primary_key: 'recurly_subscription_uuid'
|
has_many :recurly_transactions, class_name: 'JamRuby::RecurlyTransactionWebHook', inverse_of: :sale_line_item, foreign_key: 'subscription_id', primary_key: 'recurly_subscription_uuid'
|
||||||
|
|
||||||
validates :product_type, inclusion: {in: [JAMBLASTER, JAMCLOUD, JAMTRACK]}
|
validates :product_type, inclusion: {in: [JAMBLASTER, JAMCLOUD, JAMTRACK]}
|
||||||
|
|
@ -16,6 +17,7 @@ module JamRuby
|
||||||
validates :free, numericality: {only_integer: true}
|
validates :free, numericality: {only_integer: true}
|
||||||
validates :sales_tax, numericality: {only_integer: false}, allow_nil: true
|
validates :sales_tax, numericality: {only_integer: false}, allow_nil: true
|
||||||
validates :shipping_handling, numericality: {only_integer: false}
|
validates :shipping_handling, numericality: {only_integer: false}
|
||||||
|
validates :affiliate_referral_fee_in_cents, numericality: {only_integer: false}, allow_nil: true
|
||||||
validates :recurly_plan_code, presence:true
|
validates :recurly_plan_code, presence:true
|
||||||
validates :sale, presence:true
|
validates :sale, presence:true
|
||||||
|
|
||||||
|
|
@ -76,6 +78,16 @@ module JamRuby
|
||||||
sale_line_item.recurly_subscription_uuid = recurly_subscription_uuid
|
sale_line_item.recurly_subscription_uuid = recurly_subscription_uuid
|
||||||
sale_line_item.recurly_adjustment_uuid = recurly_adjustment_uuid
|
sale_line_item.recurly_adjustment_uuid = recurly_adjustment_uuid
|
||||||
sale_line_item.recurly_adjustment_credit_uuid = recurly_adjustment_credit_uuid
|
sale_line_item.recurly_adjustment_credit_uuid = recurly_adjustment_credit_uuid
|
||||||
|
|
||||||
|
# determine if we need to associate this sale with a partner
|
||||||
|
user = shopping_cart.user
|
||||||
|
referral_info = user.should_attribute_sale?(shopping_cart)
|
||||||
|
|
||||||
|
if referral_info
|
||||||
|
sale_line_item.affiliate_referral = user.affiliate_referral
|
||||||
|
sale_line_item.affiliate_referral_fee_in_cents = referral_info[:fee_in_cents]
|
||||||
|
end
|
||||||
|
|
||||||
sale.sale_line_items << sale_line_item
|
sale.sale_line_items << sale_line_item
|
||||||
sale_line_item.save
|
sale_line_item.save
|
||||||
sale_line_item
|
sale_line_item
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ module JamRuby
|
||||||
has_many :event_sessions, :class_name => "JamRuby::EventSession"
|
has_many :event_sessions, :class_name => "JamRuby::EventSession"
|
||||||
|
|
||||||
# affiliate_partner
|
# affiliate_partner
|
||||||
has_one :affiliate_partner, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :partner_user_id
|
has_one :affiliate_partner, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :partner_user_id, inverse_of: :partner_user
|
||||||
belongs_to :affiliate_referral, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :affiliate_referral_id, :counter_cache => :referral_user_count
|
belongs_to :affiliate_referral, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :affiliate_referral_id, :counter_cache => :referral_user_count
|
||||||
# diagnostics
|
# diagnostics
|
||||||
has_many :diagnostics, :class_name => "JamRuby::Diagnostic"
|
has_many :diagnostics, :class_name => "JamRuby::Diagnostic"
|
||||||
|
|
@ -991,6 +991,7 @@ module JamRuby
|
||||||
any_user = options[:any_user]
|
any_user = options[:any_user]
|
||||||
reuse_card = options[:reuse_card]
|
reuse_card = options[:reuse_card]
|
||||||
signup_hint = options[:signup_hint]
|
signup_hint = options[:signup_hint]
|
||||||
|
affiliate_partner = options[:affiliate_partner]
|
||||||
|
|
||||||
user = User.new
|
user = User.new
|
||||||
|
|
||||||
|
|
@ -1125,6 +1126,14 @@ module JamRuby
|
||||||
if user.errors.any?
|
if user.errors.any?
|
||||||
raise ActiveRecord::Rollback
|
raise ActiveRecord::Rollback
|
||||||
else
|
else
|
||||||
|
# if the partner ID was present and the partner doesn't already have a user associated, associate this new user with the affiliate partner
|
||||||
|
if affiliate_partner && affiliate_partner.partner_user.nil?
|
||||||
|
affiliate_partner.partner_user = user
|
||||||
|
unless affiliate_partner.save
|
||||||
|
@@log.error("unable to associate #{user.to_s} with affiliate_partner #{affiliate_partner.id} / #{affiliate_partner.partner_name}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if user.affiliate_referral = AffiliatePartner.find_by_id(affiliate_referral_id)
|
if user.affiliate_referral = AffiliatePartner.find_by_id(affiliate_referral_id)
|
||||||
user.save
|
user.save
|
||||||
end if affiliate_referral_id.present?
|
end if affiliate_referral_id.present?
|
||||||
|
|
@ -1640,6 +1649,15 @@ module JamRuby
|
||||||
options
|
options
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def should_attribute_sale?(shopping_cart)
|
||||||
|
if affiliate_referral
|
||||||
|
referral_info = affiliate_referral.should_attribute_sale?(shopping_cart)
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
private
|
private
|
||||||
def create_remember_token
|
def create_remember_token
|
||||||
self.remember_token = SecureRandom.urlsafe_base64
|
self.remember_token = SecureRandom.urlsafe_base64
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
require 'json'
|
||||||
|
require 'resque'
|
||||||
|
require 'resque-retry'
|
||||||
|
require 'net/http'
|
||||||
|
require 'digest/md5'
|
||||||
|
|
||||||
|
module JamRuby
|
||||||
|
|
||||||
|
# periodically scheduled to find jobs that need retrying
|
||||||
|
class TallyAffiliates
|
||||||
|
extend Resque::Plugins::JamLonelyJob
|
||||||
|
|
||||||
|
@queue = :tally_affiliates
|
||||||
|
|
||||||
|
@@log = Logging.logger[TallyAffiliates]
|
||||||
|
|
||||||
|
def self.lock_timeout
|
||||||
|
# this should be enough time to make sure the job has finished, but not so long that the system isn't recovering from a abandoned job
|
||||||
|
120
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.perform
|
||||||
|
@@log.debug("waking up")
|
||||||
|
|
||||||
|
AffiliatePartner.tally_up(Date.today)
|
||||||
|
|
||||||
|
@@log.debug("done")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -797,4 +797,33 @@ FactoryGirl.define do
|
||||||
transaction_type JamRuby::RecurlyTransactionWebHook::FAILED_PAYMENT
|
transaction_type JamRuby::RecurlyTransactionWebHook::FAILED_PAYMENT
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :affiliate_partner, class: 'JamRuby::AffiliatePartner' do
|
||||||
|
sequence(:partner_name) { |n| "partner-#{n}" }
|
||||||
|
entity_type 'Individual'
|
||||||
|
signed_at Time.now
|
||||||
|
association :partner_user, factory: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
factory :affiliate_quarterly_payment, class: 'JamRuby::AffiliateQuarterlyPayment' do
|
||||||
|
year 2015
|
||||||
|
quarter 0
|
||||||
|
association :affiliate_partner, factory: :affiliate_partner
|
||||||
|
end
|
||||||
|
|
||||||
|
factory :affiliate_monthly_payment, class: 'JamRuby::AffiliateMonthlyPayment' do
|
||||||
|
year 2015
|
||||||
|
month 0
|
||||||
|
association :affiliate_partner, factory: :affiliate_partner
|
||||||
|
end
|
||||||
|
|
||||||
|
factory :affiliate_referral_visit, class: 'JamRuby::AffiliateReferralVisit' do
|
||||||
|
ip_address '1.1.1.1'
|
||||||
|
association :affiliate_partner, factory: :affiliate_partner
|
||||||
|
end
|
||||||
|
|
||||||
|
factory :affiliate_legalese, class: 'JamRuby::AffiliateLegalese' do
|
||||||
|
legalese Faker::Lorem.paragraphs(6).join("\n\n")
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,47 +3,46 @@ require 'spec_helper'
|
||||||
describe AffiliatePartner do
|
describe AffiliatePartner do
|
||||||
|
|
||||||
let!(:user) { FactoryGirl.create(:user) }
|
let!(:user) { FactoryGirl.create(:user) }
|
||||||
let!(:partner) {
|
let(:partner) { FactoryGirl.create(:affiliate_partner) }
|
||||||
AffiliatePartner.create_with_params({:partner_name => 'partner',
|
let!(:legalese) { FactoryGirl.create(:affiliate_legalese) }
|
||||||
:partner_code => 'code',
|
let(:jam_track) {FactoryGirl.create(:jam_track) }
|
||||||
:user_email => user.email})
|
|
||||||
}
|
|
||||||
|
|
||||||
# Faker::Lorem.word is tripping up the PARTNER_CODE_REGEX. We should not use it.
|
describe "unpaid" do
|
||||||
it 'validates required fields' do
|
it "succeeds with no data" do
|
||||||
pending
|
AffiliatePartner.unpaid.length.should eq(0)
|
||||||
expect(partner.referral_user_count).to eq(0)
|
end
|
||||||
expect(partner.partner_user).to eq(user)
|
|
||||||
user.reload
|
|
||||||
expect(user.affiliate_partner).to eq(partner)
|
|
||||||
|
|
||||||
oo = AffiliatePartner.create_with_params({:partner_name => Faker::Company.name,
|
it "finds one unpaid partner" do
|
||||||
:partner_code => 'a',
|
quarter = FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner: partner, closed:true, paid:false, due_amount_in_cents: AffiliatePartner::PAY_THRESHOLD)
|
||||||
:user_email => user.email})
|
AffiliatePartner.unpaid.should eq([partner])
|
||||||
expect(oo.errors.messages[:partner_code][0]).to eq('is invalid')
|
end
|
||||||
oo = AffiliatePartner.create_with_params({:partner_name => Faker::Company.name,
|
|
||||||
:partner_code => 'foo bar',
|
|
||||||
:user_email => user.email})
|
|
||||||
expect(oo.errors.messages[:partner_code][0]).to eq('is invalid')
|
|
||||||
oo = AffiliatePartner.create_with_params({:partner_name => '',
|
|
||||||
:partner_code => Faker::Lorem.word,
|
|
||||||
:user_email => user.email})
|
|
||||||
expect(oo.errors.messages[:partner_name][0]).to eq("can't be blank")
|
|
||||||
oo = AffiliatePartner.create_with_params({:partner_name => '',
|
|
||||||
:partner_code => Faker::Lorem.word,
|
|
||||||
:user_email => Faker::Internet.email})
|
|
||||||
expect(oo.errors.messages[:partner_user][0]).to eq("can't be blank")
|
|
||||||
|
|
||||||
code = Faker::Lorem.word.upcase
|
it "finds one unpaid partner with two quarters that exceed threshold" do
|
||||||
oo = AffiliatePartner.create_with_params({:partner_name => Faker::Company.name,
|
# this $5 quarter is not enough to make the threshold
|
||||||
:partner_code => " #{code} ",
|
quarter = FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner: partner, year:2016, closed:true, paid:false, due_amount_in_cents: AffiliatePartner::PAY_THRESHOLD / 2)
|
||||||
:user_email => user.email})
|
AffiliatePartner.unpaid.should eq([])
|
||||||
expect(oo.partner_code).to eq(code.downcase)
|
|
||||||
|
# this should get the user over the hump
|
||||||
|
quarter = FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner: partner, year:2015, closed:true, paid:false, due_amount_in_cents: AffiliatePartner::PAY_THRESHOLD / 2)
|
||||||
|
AffiliatePartner.unpaid.should eq([partner])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not find paid or closed quarters" do
|
||||||
|
quarter = FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner: partner, year:2016, closed:true, paid:true, due_amount_in_cents: AffiliatePartner::PAY_THRESHOLD)
|
||||||
|
AffiliatePartner.unpaid.should eq([])
|
||||||
|
|
||||||
|
quarter = FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner: partner, year:2015, closed:false, paid:true, due_amount_in_cents: AffiliatePartner::PAY_THRESHOLD)
|
||||||
|
AffiliatePartner.unpaid.should eq([])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "user-partner association" do
|
||||||
|
user_partner = FactoryGirl.create(:user, affiliate_partner: partner)
|
||||||
|
user_partner.affiliate_partner.should_not be_nil
|
||||||
|
user_partner.affiliate_partner.present?.should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has user referrals' do
|
it 'has user referrals' do
|
||||||
pending
|
|
||||||
expect(AffiliatePartner.coded_id(partner.partner_code)).to eq(partner.id)
|
|
||||||
expect(partner.referral_user_count).to eq(0)
|
expect(partner.referral_user_count).to eq(0)
|
||||||
uu = FactoryGirl.create(:user)
|
uu = FactoryGirl.create(:user)
|
||||||
uu.affiliate_referral = partner
|
uu.affiliate_referral = partner
|
||||||
|
|
@ -73,4 +72,766 @@ describe AffiliatePartner do
|
||||||
expect(by_date[keys.last]).to eq(2)
|
expect(by_date[keys.last]).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'updates address correctly' do
|
||||||
|
addy = partner.address.clone
|
||||||
|
addy[AffiliatePartner::KEY_ADDR1] = Faker::Address.street_address
|
||||||
|
addy[AffiliatePartner::KEY_ADDR2] = Faker::Address.secondary_address
|
||||||
|
addy[AffiliatePartner::KEY_CITY] = Faker::Address.city
|
||||||
|
addy[AffiliatePartner::KEY_STATE] = Faker::Address.state_abbr
|
||||||
|
addy[AffiliatePartner::KEY_COUNTRY] = Faker::Address.country
|
||||||
|
partner.update_address_value(AffiliatePartner::KEY_ADDR1, addy[AffiliatePartner::KEY_ADDR1])
|
||||||
|
partner.update_address_value(AffiliatePartner::KEY_ADDR2, addy[AffiliatePartner::KEY_ADDR2])
|
||||||
|
partner.update_address_value(AffiliatePartner::KEY_CITY, addy[AffiliatePartner::KEY_CITY])
|
||||||
|
partner.update_address_value(AffiliatePartner::KEY_STATE, addy[AffiliatePartner::KEY_STATE])
|
||||||
|
partner.update_address_value(AffiliatePartner::KEY_COUNTRY, addy[AffiliatePartner::KEY_COUNTRY])
|
||||||
|
|
||||||
|
expect(partner.address[AffiliatePartner::KEY_ADDR1]).to eq(addy[AffiliatePartner::KEY_ADDR1])
|
||||||
|
expect(partner.address[AffiliatePartner::KEY_ADDR2]).to eq(addy[AffiliatePartner::KEY_ADDR2])
|
||||||
|
expect(partner.address[AffiliatePartner::KEY_CITY]).to eq(addy[AffiliatePartner::KEY_CITY])
|
||||||
|
expect(partner.address[AffiliatePartner::KEY_STATE]).to eq(addy[AffiliatePartner::KEY_STATE])
|
||||||
|
expect(partner.address[AffiliatePartner::KEY_COUNTRY]).to eq(addy[AffiliatePartner::KEY_COUNTRY])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'associates legalese' do
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "should_attribute_sale?" do
|
||||||
|
|
||||||
|
it "user with no affiliate relationship" do
|
||||||
|
shopping_cart = ShoppingCart.create user, jam_track, 1
|
||||||
|
user.should_attribute_sale?(shopping_cart).should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "user with an affiliate relationship buying a jamtrack" do
|
||||||
|
user.affiliate_referral = partner
|
||||||
|
user.save!
|
||||||
|
shopping_cart = ShoppingCart.create user, jam_track, 1, false
|
||||||
|
user.should_attribute_sale?(shopping_cart).should eq({fee_in_cents:20})
|
||||||
|
end
|
||||||
|
|
||||||
|
it "user with an affiliate relationship redeeming a jamtrack" do
|
||||||
|
user.affiliate_referral = partner
|
||||||
|
user.save!
|
||||||
|
shopping_cart = ShoppingCart.create user, jam_track, 1, true
|
||||||
|
user.should_attribute_sale?(shopping_cart).should eq({fee_in_cents:0})
|
||||||
|
end
|
||||||
|
|
||||||
|
it "user with an expired affiliate relationship redeeming a jamtrack" do
|
||||||
|
user.affiliate_referral = partner
|
||||||
|
user.created_at = (365 * 2 + 1).days.ago
|
||||||
|
user.save!
|
||||||
|
shopping_cart = ShoppingCart.create user, jam_track, 1, false
|
||||||
|
user.should_attribute_sale?(shopping_cart).should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "created_within_affiliate_window" do
|
||||||
|
it "user created very recently" do
|
||||||
|
partner.created_within_affiliate_window(user, Time.now).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "user created 2 years, 1 day asgo" do
|
||||||
|
days_future = 365 * 2 + 1
|
||||||
|
|
||||||
|
partner.created_within_affiliate_window(user, days_future.days.from_now).should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "tally_up" do
|
||||||
|
let(:partner1) {FactoryGirl.create(:affiliate_partner)}
|
||||||
|
let(:partner2) {FactoryGirl.create(:affiliate_partner)}
|
||||||
|
let(:payment1) {FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner: partner1)}
|
||||||
|
let(:user_partner1) { FactoryGirl.create(:user, affiliate_referral: partner1)}
|
||||||
|
let(:user_partner2) { FactoryGirl.create(:user, affiliate_referral: partner2)}
|
||||||
|
let(:sale) {Sale.create_jam_track_sale(user_partner1)}
|
||||||
|
|
||||||
|
describe "ensure_quarters_exist" do
|
||||||
|
|
||||||
|
it "runs OK with no data" do
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates new slots" do
|
||||||
|
partner1.touch
|
||||||
|
partner2.touch
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(2)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.year.should eq(2015)
|
||||||
|
quarter.quarter.should eq(0)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.year.should eq(2015)
|
||||||
|
quarter.quarter.should eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates one slot, ignoring other" do
|
||||||
|
partner1.touch
|
||||||
|
partner2.touch
|
||||||
|
payment1.touch
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(1)
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(2)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.year.should eq(2015)
|
||||||
|
quarter.quarter.should eq(0)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.year.should eq(2015)
|
||||||
|
quarter.quarter.should eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "close_quarters" do
|
||||||
|
it "runs OK with no data" do
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(0)
|
||||||
|
AffiliatePartner.close_quarters(2015, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "ignores current quarter" do
|
||||||
|
partner1.touch
|
||||||
|
partner2.touch
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(2)
|
||||||
|
AffiliatePartner.close_quarters(2015, 0)
|
||||||
|
AffiliateQuarterlyPayment.where(closed: true).count.should eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "closes previous quarter" do
|
||||||
|
partner1.touch
|
||||||
|
partner2.touch
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(2)
|
||||||
|
AffiliatePartner.close_quarters(2015, 1)
|
||||||
|
AffiliateQuarterlyPayment.where(closed: true).count.should eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "closes previous quarter (edge case)" do
|
||||||
|
partner1.touch
|
||||||
|
partner2.touch
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2014, 3)
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(2)
|
||||||
|
AffiliatePartner.close_quarters(2015, 0)
|
||||||
|
AffiliateQuarterlyPayment.where(closed: true).count.should eq(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "tally_partner_totals" do
|
||||||
|
it "runs OK with no data" do
|
||||||
|
AffiliatePartner.tally_partner_totals
|
||||||
|
AffiliatePartner.count.should eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates partner when there is no data to tally" do
|
||||||
|
partner1.touch
|
||||||
|
partner1.cumulative_earnings_in_cents.should eq(0)
|
||||||
|
partner1.referral_user_count.should eq(0)
|
||||||
|
AffiliatePartner.tally_partner_totals
|
||||||
|
partner1.reload
|
||||||
|
partner1.cumulative_earnings_in_cents.should eq(0)
|
||||||
|
partner1.referral_user_count.should eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates referral_user_count" do
|
||||||
|
FactoryGirl.create(:user, affiliate_referral: partner1)
|
||||||
|
AffiliatePartner.tally_partner_totals
|
||||||
|
partner1.reload
|
||||||
|
partner1.referral_user_count.should eq(1)
|
||||||
|
partner1.cumulative_earnings_in_cents.should eq(0)
|
||||||
|
|
||||||
|
FactoryGirl.create(:user, affiliate_referral: partner2)
|
||||||
|
AffiliatePartner.tally_partner_totals
|
||||||
|
partner1.reload
|
||||||
|
partner1.referral_user_count.should eq(1)
|
||||||
|
partner1.cumulative_earnings_in_cents.should eq(0)
|
||||||
|
partner2.reload
|
||||||
|
partner2.referral_user_count.should eq(1)
|
||||||
|
partner2.cumulative_earnings_in_cents.should eq(0)
|
||||||
|
|
||||||
|
FactoryGirl.create(:user, affiliate_referral: partner2)
|
||||||
|
AffiliatePartner.tally_partner_totals
|
||||||
|
partner1.reload
|
||||||
|
partner1.referral_user_count.should eq(1)
|
||||||
|
partner1.cumulative_earnings_in_cents.should eq(0)
|
||||||
|
partner2.reload
|
||||||
|
partner2.referral_user_count.should eq(2)
|
||||||
|
partner2.cumulative_earnings_in_cents.should eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates cumulative_earnings_in_cents" do
|
||||||
|
FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner:partner1, year:2015, quarter:0, due_amount_in_cents: 0, closed:true, paid:true)
|
||||||
|
AffiliatePartner.tally_partner_totals
|
||||||
|
partner1.reload
|
||||||
|
partner1.referral_user_count.should eq(0)
|
||||||
|
partner1.cumulative_earnings_in_cents.should eq(0)
|
||||||
|
|
||||||
|
FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner:partner2, year:2015, quarter:0, due_amount_in_cents: 10, closed:true, paid:true)
|
||||||
|
AffiliatePartner.tally_partner_totals
|
||||||
|
partner1.reload
|
||||||
|
partner1.referral_user_count.should eq(0)
|
||||||
|
partner1.cumulative_earnings_in_cents.should eq(0)
|
||||||
|
partner2.reload
|
||||||
|
partner2.referral_user_count.should eq(0)
|
||||||
|
partner2.cumulative_earnings_in_cents.should eq(10)
|
||||||
|
|
||||||
|
FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner:partner2, year:2015, quarter:1, due_amount_in_cents: 100, closed:true, paid:true)
|
||||||
|
AffiliatePartner.tally_partner_totals
|
||||||
|
partner1.reload
|
||||||
|
partner1.referral_user_count.should eq(0)
|
||||||
|
partner1.cumulative_earnings_in_cents.should eq(0)
|
||||||
|
partner2.reload
|
||||||
|
partner2.referral_user_count.should eq(0)
|
||||||
|
partner2.cumulative_earnings_in_cents.should eq(110)
|
||||||
|
|
||||||
|
FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner:partner1, year:2015, quarter:1, due_amount_in_cents: 100, closed:true, paid:true)
|
||||||
|
AffiliatePartner.tally_partner_totals
|
||||||
|
partner1.reload
|
||||||
|
partner1.referral_user_count.should eq(0)
|
||||||
|
partner1.cumulative_earnings_in_cents.should eq(100)
|
||||||
|
partner2.reload
|
||||||
|
partner2.referral_user_count.should eq(0)
|
||||||
|
partner2.cumulative_earnings_in_cents.should eq(110)
|
||||||
|
|
||||||
|
# a paid=false quarterly payment does not yet reflect in cumulative earnings
|
||||||
|
FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner:partner1, year:2015, quarter:2, due_amount_in_cents: 1000, closed:false, paid:false)
|
||||||
|
AffiliatePartner.tally_partner_totals
|
||||||
|
partner1.reload
|
||||||
|
partner1.referral_user_count.should eq(0)
|
||||||
|
partner1.cumulative_earnings_in_cents.should eq(100)
|
||||||
|
partner2.reload
|
||||||
|
partner2.referral_user_count.should eq(0)
|
||||||
|
partner2.cumulative_earnings_in_cents.should eq(110)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "total_quarters" do
|
||||||
|
it "runs OK with no data" do
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(0)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "totals 0 with no sales data" do
|
||||||
|
partner1.touch
|
||||||
|
partner2.touch
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(2)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(0)
|
||||||
|
quarter.last_updated.should_not be_nil
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(0)
|
||||||
|
quarter.last_updated.should_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "totals with sales data" do
|
||||||
|
partner1.touch
|
||||||
|
partner2.touch
|
||||||
|
|
||||||
|
|
||||||
|
# create a freebie for partner1
|
||||||
|
shopping_cart = ShoppingCart.create user_partner1, jam_track, 1, true
|
||||||
|
freebie_sale = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, nil, nil)
|
||||||
|
freebie_sale.affiliate_referral_fee_in_cents.should eq(0)
|
||||||
|
freebie_sale.created_at = Date.new(2015, 1, 1)
|
||||||
|
freebie_sale.save!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(2)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(0)
|
||||||
|
quarter.jamtracks_sold.should eq(0)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(0)
|
||||||
|
quarter.jamtracks_sold.should eq(0)
|
||||||
|
|
||||||
|
# create a real sale for partner1
|
||||||
|
shopping_cart = ShoppingCart.create user_partner1, jam_track, 1, false
|
||||||
|
real_sale = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, nil, nil)
|
||||||
|
real_sale.affiliate_referral_fee_in_cents.should eq(20)
|
||||||
|
real_sale.created_at = Date.new(2015, 1, 1)
|
||||||
|
real_sale.save!
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(20)
|
||||||
|
quarter.jamtracks_sold.should eq(1)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(0)
|
||||||
|
quarter.jamtracks_sold.should eq(0)
|
||||||
|
|
||||||
|
# create a real sale for partner2
|
||||||
|
shopping_cart = ShoppingCart.create user_partner2, jam_track, 1, false
|
||||||
|
real_sale = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, nil, nil)
|
||||||
|
real_sale.affiliate_referral_fee_in_cents.should eq(20)
|
||||||
|
real_sale.created_at = Date.new(2015, 1, 1)
|
||||||
|
real_sale.save!
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(20)
|
||||||
|
quarter.jamtracks_sold.should eq(1)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(20)
|
||||||
|
quarter.jamtracks_sold.should eq(1)
|
||||||
|
|
||||||
|
# create a real sale for partner1
|
||||||
|
shopping_cart = ShoppingCart.create user_partner1, jam_track, 1, false
|
||||||
|
real_sale = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, nil, nil)
|
||||||
|
real_sale.affiliate_referral_fee_in_cents.should eq(20)
|
||||||
|
real_sale.created_at = Date.new(2015, 1, 1)
|
||||||
|
real_sale.save!
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(40)
|
||||||
|
quarter.jamtracks_sold.should eq(2)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(20)
|
||||||
|
quarter.jamtracks_sold.should eq(1)
|
||||||
|
|
||||||
|
|
||||||
|
# create a real sale for a non-affiliated user
|
||||||
|
shopping_cart = ShoppingCart.create user, jam_track, 1, false
|
||||||
|
real_sale = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, nil, nil)
|
||||||
|
real_sale.affiliate_referral_fee_in_cents.should be_nil
|
||||||
|
real_sale.created_at = Date.new(2015, 1, 1)
|
||||||
|
real_sale.save!
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(40)
|
||||||
|
quarter.jamtracks_sold.should eq(2)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(20)
|
||||||
|
quarter.jamtracks_sold.should eq(1)
|
||||||
|
|
||||||
|
# create a real sale but in previous quarter (should no have effect on the quarter being computed)
|
||||||
|
shopping_cart = ShoppingCart.create user_partner1, jam_track, 1, false
|
||||||
|
real_sale = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, nil, nil)
|
||||||
|
real_sale.affiliate_referral_fee_in_cents.should eq(20)
|
||||||
|
real_sale.created_at = Date.new(2014, 12, 31)
|
||||||
|
real_sale.save!
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(40)
|
||||||
|
quarter.jamtracks_sold.should eq(2)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(20)
|
||||||
|
quarter.jamtracks_sold.should eq(1)
|
||||||
|
|
||||||
|
# create a real sale but in later quarter (should no have effect on the quarter being computed)
|
||||||
|
shopping_cart = ShoppingCart.create user_partner1, jam_track, 1, false
|
||||||
|
real_sale = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, nil, nil)
|
||||||
|
real_sale.affiliate_referral_fee_in_cents.should eq(20)
|
||||||
|
real_sale.created_at = Date.new(2015, 4, 1)
|
||||||
|
real_sale.save!
|
||||||
|
real_sale_later = real_sale
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(40)
|
||||||
|
quarter.jamtracks_sold.should eq(2)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(20)
|
||||||
|
quarter.jamtracks_sold.should eq(1)
|
||||||
|
|
||||||
|
# create a real sale but then refund it
|
||||||
|
shopping_cart = ShoppingCart.create user_partner1, jam_track, 1, false
|
||||||
|
real_sale = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, nil, nil)
|
||||||
|
real_sale.affiliate_referral_fee_in_cents.should eq(20)
|
||||||
|
real_sale.created_at = Date.new(2015, 3, 31)
|
||||||
|
real_sale.save!
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(60)
|
||||||
|
quarter.jamtracks_sold.should eq(3)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(20)
|
||||||
|
# now refund it
|
||||||
|
real_sale.affiliate_refunded_at = Date.new(2015, 3, 1)
|
||||||
|
real_sale.affiliate_refunded = true
|
||||||
|
real_sale.save!
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 0)
|
||||||
|
AffiliatePartner.total_quarters(2015, 0)
|
||||||
|
quarter = partner1.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(40)
|
||||||
|
quarter = partner2.quarters.first
|
||||||
|
quarter.due_amount_in_cents.should eq(20)
|
||||||
|
quarter.jamtracks_sold.should eq(1)
|
||||||
|
|
||||||
|
|
||||||
|
# create the 2nd quarter, which should add up the sale created a few bits up
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 1)
|
||||||
|
AffiliatePartner.total_quarters(2015, 1)
|
||||||
|
payment = AffiliateQuarterlyPayment.find_by_quarter_and_year_and_affiliate_partner_id!(1, 2015, partner1.id)
|
||||||
|
payment.due_amount_in_cents.should eq(20)
|
||||||
|
|
||||||
|
# and now refund it in the 3rd quarter
|
||||||
|
real_sale_later.affiliate_refunded_at = Date.new(2015, 7, 1)
|
||||||
|
real_sale_later.affiliate_refunded = true
|
||||||
|
real_sale_later.save!
|
||||||
|
AffiliatePartner.total_quarters(2015, 1)
|
||||||
|
payment = AffiliateQuarterlyPayment.find_by_quarter_and_year_and_affiliate_partner_id!(1, 2015, partner1.id)
|
||||||
|
payment.due_amount_in_cents.should eq(20)
|
||||||
|
payment.jamtracks_sold.should eq(1)
|
||||||
|
|
||||||
|
# now catch the one refund in the 3rd quarter
|
||||||
|
AffiliatePartner.ensure_quarters_exist(2015, 2)
|
||||||
|
AffiliatePartner.total_quarters(2015, 2)
|
||||||
|
payment = AffiliateQuarterlyPayment.find_by_quarter_and_year_and_affiliate_partner_id!(2, 2015, partner1.id)
|
||||||
|
payment.due_amount_in_cents.should eq(-20)
|
||||||
|
payment.jamtracks_sold.should eq(-1)
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "tally_up complete" do
|
||||||
|
it "runs OK with no data" do
|
||||||
|
AffiliatePartner.tally_up(Date.new(2015, 1, 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "successive runs" do
|
||||||
|
GenericState.singleton.affiliate_tallied_at.should be_nil
|
||||||
|
AffiliatePartner.tally_up(Date.new(2015, 1, 1))
|
||||||
|
GenericState.singleton.affiliate_tallied_at.should_not be_nil
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(0)
|
||||||
|
|
||||||
|
# partner is created
|
||||||
|
partner1.touch
|
||||||
|
|
||||||
|
AffiliatePartner.tally_up(Date.new(2015, 1, 1))
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(2)
|
||||||
|
AffiliateMonthlyPayment.count.should eq(6)
|
||||||
|
|
||||||
|
quarter_previous = AffiliateQuarterlyPayment.find_by_quarter_and_year_and_affiliate_partner_id!(3, 2014, partner1.id)
|
||||||
|
quarter_previous.due_amount_in_cents.should eq(0)
|
||||||
|
quarter_previous.closed.should be_true
|
||||||
|
quarter = AffiliateQuarterlyPayment.find_by_quarter_and_year_and_affiliate_partner_id!(0, 2015, partner1.id)
|
||||||
|
quarter.due_amount_in_cents.should eq(0)
|
||||||
|
quarter.closed.should be_false
|
||||||
|
|
||||||
|
month_previous= AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(10, 2014, partner1.id)
|
||||||
|
month_previous.due_amount_in_cents.should eq(0)
|
||||||
|
month_previous.closed.should be_true
|
||||||
|
month_previous.jamtracks_sold.should eq(0)
|
||||||
|
month_previous= AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(11, 2014, partner1.id)
|
||||||
|
month_previous.due_amount_in_cents.should eq(0)
|
||||||
|
month_previous.closed.should be_true
|
||||||
|
month_previous.jamtracks_sold.should eq(0)
|
||||||
|
month_previous= AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(12, 2014, partner1.id)
|
||||||
|
month_previous.due_amount_in_cents.should eq(0)
|
||||||
|
month_previous.closed.should be_true
|
||||||
|
month_previous.jamtracks_sold.should eq(0)
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(1, 2015, partner1.id)
|
||||||
|
month_previous.due_amount_in_cents.should eq(0)
|
||||||
|
month_previous.closed.should be_true
|
||||||
|
month_previous.jamtracks_sold.should eq(0)
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(2, 2015, partner1.id)
|
||||||
|
month_previous.due_amount_in_cents.should eq(0)
|
||||||
|
month_previous.closed.should be_true
|
||||||
|
month.jamtracks_sold.should eq(0)
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(3, 2015, partner1.id)
|
||||||
|
month_previous.due_amount_in_cents.should eq(0)
|
||||||
|
month_previous.closed.should be_true
|
||||||
|
month_previous.jamtracks_sold.should eq(0)
|
||||||
|
|
||||||
|
|
||||||
|
shopping_cart = ShoppingCart.create user_partner1, jam_track, 1, false
|
||||||
|
real_sale = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, nil, nil)
|
||||||
|
real_sale.affiliate_referral_fee_in_cents.should eq(20)
|
||||||
|
real_sale.created_at = Date.new(2015, 4, 1)
|
||||||
|
real_sale.save!
|
||||||
|
|
||||||
|
AffiliatePartner.tally_up(Date.new(2015, 4, 1))
|
||||||
|
AffiliateQuarterlyPayment.count.should eq(3)
|
||||||
|
quarter = AffiliateQuarterlyPayment.find_by_quarter_and_year_and_affiliate_partner_id!(0, 2015, partner1.id)
|
||||||
|
quarter.due_amount_in_cents.should eq(0)
|
||||||
|
quarter.jamtracks_sold.should eq(0)
|
||||||
|
quarter.closed.should be_true
|
||||||
|
quarter2 = AffiliateQuarterlyPayment.find_by_quarter_and_year_and_affiliate_partner_id!(1, 2015, partner1.id)
|
||||||
|
quarter2.due_amount_in_cents.should eq(20)
|
||||||
|
quarter2.jamtracks_sold.should eq(1)
|
||||||
|
quarter2.closed.should be_false
|
||||||
|
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(1, 2015, partner1.id)
|
||||||
|
month.due_amount_in_cents.should eq(0)
|
||||||
|
month.jamtracks_sold.should eq(0)
|
||||||
|
month.closed.should be_true
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(2, 2015, partner1.id)
|
||||||
|
month.due_amount_in_cents.should eq(0)
|
||||||
|
month.jamtracks_sold.should eq(0)
|
||||||
|
month.closed.should be_true
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(3, 2015, partner1.id)
|
||||||
|
month.due_amount_in_cents.should eq(0)
|
||||||
|
month.jamtracks_sold.should eq(0)
|
||||||
|
month.closed.should be_true
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(4, 2015, partner1.id)
|
||||||
|
month.due_amount_in_cents.should eq(20)
|
||||||
|
month.jamtracks_sold.should eq(1)
|
||||||
|
month.closed.should be_false
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(5, 2015, partner1.id)
|
||||||
|
month.due_amount_in_cents.should eq(0)
|
||||||
|
month.jamtracks_sold.should eq(0)
|
||||||
|
month.closed.should be_false
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(6, 2015, partner1.id)
|
||||||
|
month.due_amount_in_cents.should eq(0)
|
||||||
|
month.jamtracks_sold.should eq(0)
|
||||||
|
month.closed.should be_false
|
||||||
|
|
||||||
|
# now sneak in a purchase in the 1st quarter, which makes no sense, but proves that closed quarters are not touched
|
||||||
|
|
||||||
|
shopping_cart = ShoppingCart.create user_partner1, jam_track, 1, false
|
||||||
|
real_sale = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, nil, nil)
|
||||||
|
real_sale.affiliate_referral_fee_in_cents.should eq(20)
|
||||||
|
real_sale.created_at = Date.new(2015, 1, 1)
|
||||||
|
real_sale.save!
|
||||||
|
|
||||||
|
AffiliatePartner.tally_up(Date.new(2015, 4, 2))
|
||||||
|
quarter = AffiliateQuarterlyPayment.find_by_quarter_and_year_and_affiliate_partner_id!(0, 2015, partner1.id)
|
||||||
|
quarter.due_amount_in_cents.should eq(0)
|
||||||
|
quarter.jamtracks_sold.should eq(0)
|
||||||
|
quarter.closed.should be_true
|
||||||
|
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(1, 2015, partner1.id)
|
||||||
|
month.due_amount_in_cents.should eq(0)
|
||||||
|
month.closed.should be_true
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(2, 2015, partner1.id)
|
||||||
|
month.due_amount_in_cents.should eq(0)
|
||||||
|
month.jamtracks_sold.should eq(0)
|
||||||
|
month.closed.should be_true
|
||||||
|
month = AffiliateMonthlyPayment.find_by_month_and_year_and_affiliate_partner_id!(3, 2015, partner1.id)
|
||||||
|
month.due_amount_in_cents.should eq(0)
|
||||||
|
month.jamtracks_sold.should eq(0)
|
||||||
|
month.closed.should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "tally_traffic_totals" do
|
||||||
|
|
||||||
|
it "runs OK with no data" do
|
||||||
|
AffiliatePartner.tally_traffic_totals(Date.yesterday, Date.today)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can deal with simple signup case" do
|
||||||
|
user_partner1.touch
|
||||||
|
|
||||||
|
day0 = user_partner1.created_at.to_date
|
||||||
|
|
||||||
|
# simulate what happens when this scheduled job first ever runs
|
||||||
|
AffiliatePartner.tally_traffic_totals(nil, day0)
|
||||||
|
AffiliateTrafficTotal.count.should eq(1)
|
||||||
|
traffic_total_day_before = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day0 - 1, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day_before.visits.should eq(0)
|
||||||
|
traffic_total_day_before.signups.should eq(0)
|
||||||
|
|
||||||
|
# then simulate when it runs on the same day as it ran on the day before
|
||||||
|
AffiliatePartner.tally_traffic_totals(day0, day0)
|
||||||
|
AffiliateTrafficTotal.count.should eq(1)
|
||||||
|
traffic_total_day_before = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day0 - 1, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day_before.visits.should eq(0)
|
||||||
|
traffic_total_day_before.signups.should eq(0)
|
||||||
|
|
||||||
|
# now run it on the next day, which should catch the signup event
|
||||||
|
|
||||||
|
day1 = day0 + 1
|
||||||
|
AffiliatePartner.tally_traffic_totals(day0, day1)
|
||||||
|
AffiliateTrafficTotal.count.should eq(2)
|
||||||
|
traffic_total_day_before = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day0 - 1, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day_before.visits.should eq(0)
|
||||||
|
traffic_total_day_before.signups.should eq(0)
|
||||||
|
traffic_total_day0 = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day0, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day0.visits.should eq(0)
|
||||||
|
traffic_total_day0.signups.should eq(1)
|
||||||
|
|
||||||
|
# add in a visit
|
||||||
|
visit = FactoryGirl.create(:affiliate_referral_visit, affiliate_partner: user_partner1.affiliate_referral)
|
||||||
|
|
||||||
|
# it won't get seen though because we've moved on
|
||||||
|
AffiliatePartner.tally_traffic_totals(day1, day1)
|
||||||
|
AffiliateTrafficTotal.count.should eq(2)
|
||||||
|
traffic_total_day_before = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day0 - 1, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day_before.visits.should eq(0)
|
||||||
|
traffic_total_day_before.signups.should eq(0)
|
||||||
|
traffic_total_day0 = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day0, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day0.visits.should eq(0)
|
||||||
|
traffic_total_day0.signups.should eq(1)
|
||||||
|
|
||||||
|
# manipulate the visit created_at so we can record it
|
||||||
|
visit.created_at = day1
|
||||||
|
visit.save!
|
||||||
|
day2 = day1 + 1
|
||||||
|
AffiliatePartner.tally_traffic_totals(day1, day2)
|
||||||
|
AffiliateTrafficTotal.count.should eq(3)
|
||||||
|
traffic_total_day_before = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day0 - 1, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day_before.visits.should eq(0)
|
||||||
|
traffic_total_day_before.signups.should eq(0)
|
||||||
|
traffic_total_day0 = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day0, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day0.visits.should eq(0)
|
||||||
|
traffic_total_day0.signups.should eq(1)
|
||||||
|
traffic_total_day1 = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day1, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day1.visits.should eq(1)
|
||||||
|
traffic_total_day1.signups.should eq(0)
|
||||||
|
|
||||||
|
# now create 2 records on day 2 for visits and signups both, and a partner with their own visit and signup, and do a final check
|
||||||
|
|
||||||
|
user_partner2.touch
|
||||||
|
|
||||||
|
visit2 = FactoryGirl.create(:affiliate_referral_visit, affiliate_partner: user_partner1.affiliate_referral)
|
||||||
|
visit3 = FactoryGirl.create(:affiliate_referral_visit, affiliate_partner: user_partner1.affiliate_referral)
|
||||||
|
visit_partner2 = FactoryGirl.create(:affiliate_referral_visit, affiliate_partner: user_partner2.affiliate_referral)
|
||||||
|
visit2.created_at = day2
|
||||||
|
visit3.created_at = day2
|
||||||
|
visit_partner2.created_at = day2
|
||||||
|
visit2.save!
|
||||||
|
visit3.save!
|
||||||
|
visit_partner2.save!
|
||||||
|
|
||||||
|
user2 = FactoryGirl.create(:user, affiliate_referral:user_partner1.affiliate_referral)
|
||||||
|
user3 = FactoryGirl.create(:user, affiliate_referral:user_partner1.affiliate_referral)
|
||||||
|
user2.created_at = day2
|
||||||
|
user3.created_at = day2
|
||||||
|
user_partner2.created_at = day2
|
||||||
|
user2.save!
|
||||||
|
user3.save!
|
||||||
|
user_partner2.save!
|
||||||
|
|
||||||
|
|
||||||
|
day3 = day2 + 1
|
||||||
|
AffiliatePartner.tally_traffic_totals(day2, day3)
|
||||||
|
AffiliateTrafficTotal.count.should eq(5)
|
||||||
|
traffic_total_day_before = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day0 - 1, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day_before.visits.should eq(0)
|
||||||
|
traffic_total_day_before.signups.should eq(0)
|
||||||
|
traffic_total_day0 = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day0, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day0.visits.should eq(0)
|
||||||
|
traffic_total_day0.signups.should eq(1)
|
||||||
|
traffic_total_day1 = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day1, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day1.visits.should eq(1)
|
||||||
|
traffic_total_day1.signups.should eq(0)
|
||||||
|
traffic_total_day2 = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day2, user_partner1.affiliate_referral_id)
|
||||||
|
traffic_total_day2.visits.should eq(2)
|
||||||
|
traffic_total_day2.signups.should eq(2)
|
||||||
|
traffic_total_day2 = AffiliateTrafficTotal.find_by_day_and_affiliate_partner_id(day2, user_partner2.affiliate_referral_id)
|
||||||
|
traffic_total_day2.visits.should eq(1)
|
||||||
|
traffic_total_day2.signups.should eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "boundary_dates" do
|
||||||
|
it "1st quarter" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates(2015, 0)
|
||||||
|
start_date.should eq(Date.new(2015, 1, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 3, 31))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "2nd quarter" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates(2015, 1)
|
||||||
|
start_date.should eq(Date.new(2015, 4, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 6, 30))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "3rd quarter" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates(2015, 2)
|
||||||
|
start_date.should eq(Date.new(2015, 7, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 9, 30))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "4th quarter" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates(2015, 3)
|
||||||
|
start_date.should eq(Date.new(2015, 10, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 12, 31))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "boundary_dates_for_month" do
|
||||||
|
it "invalid month" do
|
||||||
|
expect{AffiliatePartner.boundary_dates_for_month(2015, 0)}.to raise_error
|
||||||
|
end
|
||||||
|
|
||||||
|
it "January" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 1)
|
||||||
|
start_date.should eq(Date.new(2015, 1, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 1, 31))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "February" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 2)
|
||||||
|
start_date.should eq(Date.new(2015, 2, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 2, 28))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "March" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 3)
|
||||||
|
start_date.should eq(Date.new(2015, 3, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 3, 31))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "April" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 4)
|
||||||
|
start_date.should eq(Date.new(2015, 4, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 4, 30))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "May" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 5)
|
||||||
|
start_date.should eq(Date.new(2015, 5, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 5, 31))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "June" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 6)
|
||||||
|
start_date.should eq(Date.new(2015, 6, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 6, 30))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "July" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 7)
|
||||||
|
start_date.should eq(Date.new(2015, 7, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 7, 31))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "August" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 8)
|
||||||
|
start_date.should eq(Date.new(2015, 8, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 8, 31))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "September" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 9)
|
||||||
|
start_date.should eq(Date.new(2015, 9, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 9, 30))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "October" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 10)
|
||||||
|
start_date.should eq(Date.new(2015, 10, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 10, 31))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "November" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 11)
|
||||||
|
start_date.should eq(Date.new(2015, 11, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 11, 30))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "December" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2015, 12)
|
||||||
|
start_date.should eq(Date.new(2015, 12, 1))
|
||||||
|
end_date.should eq(Date.new(2015, 12, 31))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "February in a leap year" do
|
||||||
|
start_date, end_date = AffiliatePartner.boundary_dates_for_month(2016, 2)
|
||||||
|
start_date.should eq(Date.new(2016, 2, 1))
|
||||||
|
end_date.should eq(Date.new(2016, 2, 29))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe AffiliatePayment do
|
||||||
|
|
||||||
|
let(:partner) { FactoryGirl.create(:affiliate_partner) }
|
||||||
|
let(:user_partner) { FactoryGirl.create(:user, affiliate_partner: partner) }
|
||||||
|
|
||||||
|
it "succeeds with no data" do
|
||||||
|
results, nex = AffiliatePayment.index(user_partner, {})
|
||||||
|
results.length.should eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sorts month and quarters correctly" do
|
||||||
|
monthly1 = FactoryGirl.create(:affiliate_monthly_payment, affiliate_partner: partner, closed: true, due_amount_in_cents: 10, month: 1, year: 2015)
|
||||||
|
monthly2 = FactoryGirl.create(:affiliate_monthly_payment, affiliate_partner: partner, closed: true, due_amount_in_cents: 20, month: 2, year: 2015)
|
||||||
|
monthly3 = FactoryGirl.create(:affiliate_monthly_payment, affiliate_partner: partner, closed: true, due_amount_in_cents: 30, month: 3, year: 2015)
|
||||||
|
monthly4 = FactoryGirl.create(:affiliate_monthly_payment, affiliate_partner: partner, closed: true, due_amount_in_cents: 40, month: 4, year: 2015)
|
||||||
|
quarterly = FactoryGirl.create(:affiliate_quarterly_payment, affiliate_partner: partner, closed: true, paid:true, due_amount_in_cents: 50, quarter: 0, year: 2015)
|
||||||
|
results, nex = AffiliatePayment.index(user_partner, {})
|
||||||
|
results.length.should eq(5)
|
||||||
|
result1 = results[0]
|
||||||
|
result2 = results[1]
|
||||||
|
result3 = results[2]
|
||||||
|
result4 = results[3]
|
||||||
|
result5 = results[4]
|
||||||
|
|
||||||
|
result1.payment_type.should eq('monthly')
|
||||||
|
result1.due_amount_in_cents.should eq(10)
|
||||||
|
result2.payment_type.should eq('monthly')
|
||||||
|
result2.due_amount_in_cents.should eq(20)
|
||||||
|
result3.payment_type.should eq('monthly')
|
||||||
|
result3.due_amount_in_cents.should eq(30)
|
||||||
|
result4.payment_type.should eq('quarterly')
|
||||||
|
result4.due_amount_in_cents.should eq(50)
|
||||||
|
result5.payment_type.should eq('monthly')
|
||||||
|
result5.due_amount_in_cents.should eq(40)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe AffiliateReferralVisit do
|
||||||
|
|
||||||
|
let!(:user) { FactoryGirl.create(:user) }
|
||||||
|
let(:partner) { FactoryGirl.create(:affiliate_partner) }
|
||||||
|
let(:valid_track_options) {
|
||||||
|
{
|
||||||
|
affiliate_id: partner.id,
|
||||||
|
visited: false,
|
||||||
|
remote_ip: '1.2.2.1',
|
||||||
|
visited_url: '/',
|
||||||
|
referral_url: 'http://www.youtube.com',
|
||||||
|
current_user: nil
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe "track" do
|
||||||
|
it "succeeds" do
|
||||||
|
visit = AffiliateReferralVisit.track( valid_track_options )
|
||||||
|
visit.valid?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "never fails with error" do
|
||||||
|
visit = AffiliateReferralVisit.track( {})
|
||||||
|
visit.valid?.should be_false
|
||||||
|
|
||||||
|
options = valid_track_options
|
||||||
|
options[:affiliate_id] = 111
|
||||||
|
visit = AffiliateReferralVisit.track( options)
|
||||||
|
visit.valid?.should be_true
|
||||||
|
|
||||||
|
options = valid_track_options
|
||||||
|
options[:current_user] = user
|
||||||
|
visit = AffiliateReferralVisit.track( options)
|
||||||
|
visit.valid?.should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -168,7 +168,8 @@ describe JamTrackRight do
|
||||||
JamTrackRight.stats.should eq('count' => 1,
|
JamTrackRight.stats.should eq('count' => 1,
|
||||||
'signing_count' => 0,
|
'signing_count' => 0,
|
||||||
'redeemed_count' => 0,
|
'redeemed_count' => 0,
|
||||||
'purchased_count' => 1)
|
'purchased_count' => 1,
|
||||||
|
'redeemed_and_dl_count' => 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "two" do
|
it "two" do
|
||||||
|
|
@ -178,7 +179,8 @@ describe JamTrackRight do
|
||||||
JamTrackRight.stats.should eq('count' => 2,
|
JamTrackRight.stats.should eq('count' => 2,
|
||||||
'signing_count' => 0,
|
'signing_count' => 0,
|
||||||
'redeemed_count' => 0,
|
'redeemed_count' => 0,
|
||||||
'purchased_count' => 2)
|
'purchased_count' => 2,
|
||||||
|
'redeemed_and_dl_count' => 0)
|
||||||
|
|
||||||
right1.signing_44 = true
|
right1.signing_44 = true
|
||||||
right1.save!
|
right1.save!
|
||||||
|
|
@ -188,7 +190,8 @@ describe JamTrackRight do
|
||||||
JamTrackRight.stats.should eq('count' => 2,
|
JamTrackRight.stats.should eq('count' => 2,
|
||||||
'signing_count' => 2,
|
'signing_count' => 2,
|
||||||
'redeemed_count' => 0,
|
'redeemed_count' => 0,
|
||||||
'purchased_count' => 2)
|
'purchased_count' => 2,
|
||||||
|
'redeemed_and_dl_count' => 0)
|
||||||
|
|
||||||
right1.redeemed = true
|
right1.redeemed = true
|
||||||
right1.save!
|
right1.save!
|
||||||
|
|
@ -196,7 +199,8 @@ describe JamTrackRight do
|
||||||
JamTrackRight.stats.should eq('count' => 2,
|
JamTrackRight.stats.should eq('count' => 2,
|
||||||
'signing_count' => 2,
|
'signing_count' => 2,
|
||||||
'redeemed_count' => 1,
|
'redeemed_count' => 1,
|
||||||
'purchased_count' => 1)
|
'purchased_count' => 1,
|
||||||
|
'redeemed_and_dl_count' => 0)
|
||||||
|
|
||||||
right2.is_test_purchase = true
|
right2.is_test_purchase = true
|
||||||
right2.save!
|
right2.save!
|
||||||
|
|
@ -204,7 +208,8 @@ describe JamTrackRight do
|
||||||
JamTrackRight.stats.should eq('count' => 1,
|
JamTrackRight.stats.should eq('count' => 1,
|
||||||
'signing_count' => 1,
|
'signing_count' => 1,
|
||||||
'redeemed_count' => 1,
|
'redeemed_count' => 1,
|
||||||
'purchased_count' => 0)
|
'purchased_count' => 0,
|
||||||
|
'redeemed_and_dl_count' => 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -213,7 +218,9 @@ describe JamTrackRight do
|
||||||
let(:other) {FactoryGirl.create(:user)}
|
let(:other) {FactoryGirl.create(:user)}
|
||||||
let(:first_fingerprint) { {all: 'all', running: 'running' } }
|
let(:first_fingerprint) { {all: 'all', running: 'running' } }
|
||||||
let(:new_fingerprint) { {all: 'all_2', running: 'running' } }
|
let(:new_fingerprint) { {all: 'all_2', running: 'running' } }
|
||||||
|
let(:full_fingerprint) { {all: :all_3, running: :running_3, all_3: { mac: "72:00:02:4C:1E:61", name: "en2", upstate: true }, running_3: { mac: "72:00:02:4C:1E:62", name: "en3", upstate: false } } }
|
||||||
let(:remote_ip) {'1.1.1.1'}
|
let(:remote_ip) {'1.1.1.1'}
|
||||||
|
let(:remote_ip2) {'2.2.2.2'}
|
||||||
let(:jam_track_right) { FactoryGirl.create(:jam_track_right, user: user, redeemed: true, redeemed_and_fingerprinted: false) }
|
let(:jam_track_right) { FactoryGirl.create(:jam_track_right, user: user, redeemed: true, redeemed_and_fingerprinted: false) }
|
||||||
let(:jam_track_right_purchased) { FactoryGirl.create(:jam_track_right, user: user, redeemed: false, redeemed_and_fingerprinted: false) }
|
let(:jam_track_right_purchased) { FactoryGirl.create(:jam_track_right, user: user, redeemed: false, redeemed_and_fingerprinted: false) }
|
||||||
let(:jam_track_right_other) { FactoryGirl.create(:jam_track_right, user: other, redeemed: true, redeemed_and_fingerprinted: false) }
|
let(:jam_track_right_other) { FactoryGirl.create(:jam_track_right, user: other, redeemed: true, redeemed_and_fingerprinted: false) }
|
||||||
|
|
@ -233,16 +240,16 @@ describe JamTrackRight do
|
||||||
jam_track_right.redeemed_and_fingerprinted.should be_true
|
jam_track_right.redeemed_and_fingerprinted.should be_true
|
||||||
|
|
||||||
|
|
||||||
mf = MachineFingerprint.find_by_fingerprint(first_fingerprint[:all])
|
mf = MachineFingerprint.find_by_fingerprint('all')
|
||||||
mf.user.should eq(user)
|
mf.user.should eq(user)
|
||||||
mf.fingerprint.should eq(first_fingerprint[:all])
|
mf.fingerprint.should eq('all')
|
||||||
mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
|
mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
|
||||||
mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ALL)
|
mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ALL)
|
||||||
mf.jam_track_right.should eq(jam_track_right)
|
mf.jam_track_right.should eq(jam_track_right)
|
||||||
|
|
||||||
mf = MachineFingerprint.find_by_fingerprint(first_fingerprint[:running])
|
mf = MachineFingerprint.find_by_fingerprint('running')
|
||||||
mf.user.should eq(user)
|
mf.user.should eq(user)
|
||||||
mf.fingerprint.should eq(first_fingerprint[:running])
|
mf.fingerprint.should eq('running')
|
||||||
mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
|
mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
|
||||||
mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ACTIVE)
|
mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ACTIVE)
|
||||||
mf.jam_track_right.should eq(jam_track_right)
|
mf.jam_track_right.should eq(jam_track_right)
|
||||||
|
|
@ -274,40 +281,80 @@ describe JamTrackRight do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "protects against re-using fingerprint across users (conflicts on all fp)" do
|
it "protects against re-using fingerprint across users (conflicts on all fp)" do
|
||||||
|
first_fingerprint2 = first_fingerprint.clone
|
||||||
jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
|
jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
|
||||||
MachineFingerprint.count.should eq(2)
|
MachineFingerprint.count.should eq(2)
|
||||||
first_fingerprint[:running] = 'running_2'
|
first_fingerprint2[:running] = 'running_2'
|
||||||
jam_track_right_other.guard_against_fraud(other, first_fingerprint, remote_ip).should eq("other user has 'all' fingerprint")
|
jam_track_right_other.guard_against_fraud(other, first_fingerprint2, remote_ip).should eq("other user has 'all' fingerprint")
|
||||||
|
|
||||||
mf = MachineFingerprint.find_by_fingerprint(first_fingerprint[:running])
|
mf = MachineFingerprint.find_by_fingerprint('running')
|
||||||
mf.user.should eq(other)
|
mf.user.should eq(user)
|
||||||
mf.fingerprint.should eq(first_fingerprint[:running])
|
mf.fingerprint.should eq('running')
|
||||||
mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_FRAUD_CONFLICT)
|
mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
|
||||||
mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ACTIVE)
|
mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ACTIVE)
|
||||||
mf.jam_track_right.should eq(jam_track_right_other)
|
mf.jam_track_right.should eq(jam_track_right)
|
||||||
MachineFingerprint.count.should eq(3)
|
MachineFingerprint.count.should eq(4)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "protects against re-using fingerprint across users (conflicts on running fp)" do
|
it "protects against re-using fingerprint across users (conflicts on running fp)" do
|
||||||
|
first_fingerprint2 = first_fingerprint.clone
|
||||||
jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
|
jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
|
||||||
MachineFingerprint.count.should eq(2)
|
MachineFingerprint.count.should eq(2)
|
||||||
first_fingerprint[:all] = 'all_2'
|
first_fingerprint2[:all] = 'all_2'
|
||||||
jam_track_right_other.guard_against_fraud(other, first_fingerprint, remote_ip).should eq("other user has 'running' fingerprint")
|
jam_track_right_other.guard_against_fraud(other, first_fingerprint2, remote_ip).should eq("other user has 'running' fingerprint")
|
||||||
|
|
||||||
mf = MachineFingerprint.find_by_fingerprint(first_fingerprint[:all])
|
mf = MachineFingerprint.find_by_fingerprint('all')
|
||||||
mf.user.should eq(other)
|
mf.user.should eq(user)
|
||||||
mf.fingerprint.should eq(first_fingerprint[:all])
|
mf.fingerprint.should eq('all')
|
||||||
mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_FRAUD_CONFLICT)
|
mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
|
||||||
mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ALL)
|
mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ALL)
|
||||||
mf.jam_track_right.should eq(jam_track_right_other)
|
mf.jam_track_right.should eq(jam_track_right)
|
||||||
MachineFingerprint.count.should eq(3)
|
MachineFingerprint.count.should eq(4)
|
||||||
|
|
||||||
|
FraudAlert.count.should eq(1)
|
||||||
|
fraud = FraudAlert.first
|
||||||
|
fraud.user.should eq(other)
|
||||||
|
fraud.machine_fingerprint.should eq(MachineFingerprint.where(fingerprint:'running').where(user_id:other.id).first)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "ignores whitelisted fingerprint" do
|
||||||
|
whitelist = FingerprintWhitelist.new
|
||||||
|
whitelist.fingerprint = first_fingerprint[:running]
|
||||||
|
whitelist.save!
|
||||||
|
|
||||||
|
first_fingerprint2 = first_fingerprint.clone
|
||||||
|
jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
|
||||||
|
MachineFingerprint.count.should eq(2)
|
||||||
|
first_fingerprint2[:all] = 'all_2'
|
||||||
|
jam_track_right_other.guard_against_fraud(other, first_fingerprint2, remote_ip).should be_nil
|
||||||
|
|
||||||
|
FraudAlert.count.should eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not conflict if same mac, but different IP address" do
|
||||||
|
first_fingerprint2 = first_fingerprint.clone
|
||||||
|
jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
|
||||||
|
MachineFingerprint.count.should eq(2)
|
||||||
|
first_fingerprint2[:all] = 'all_2'
|
||||||
|
jam_track_right_other.guard_against_fraud(other, first_fingerprint2, remote_ip2).should eq(nil)
|
||||||
|
|
||||||
|
mf = MachineFingerprint.find_by_fingerprint('all')
|
||||||
|
mf.user.should eq(user)
|
||||||
|
mf.fingerprint.should eq('all')
|
||||||
|
mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
|
||||||
|
mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ALL)
|
||||||
|
mf.jam_track_right.should eq(jam_track_right)
|
||||||
|
MachineFingerprint.count.should eq(4)
|
||||||
|
|
||||||
|
FraudAlert.count.should eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
# if you try to buy a regular jamtrack with a fingerprint belonging to another user? so what. you paid for it
|
# if you try to buy a regular jamtrack with a fingerprint belonging to another user? so what. you paid for it
|
||||||
it "allows re-use of fingerprint if jamtrack is a normal purchase" do
|
it "allows re-use of fingerprint if jamtrack is a normal purchase" do
|
||||||
|
first_fingerprint2 = first_fingerprint.clone
|
||||||
jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
|
jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
|
||||||
MachineFingerprint.count.should eq(2)
|
MachineFingerprint.count.should eq(2)
|
||||||
jam_track_right_other_purchased.guard_against_fraud(other, first_fingerprint, remote_ip).should be_nil
|
jam_track_right_other_purchased.guard_against_fraud(other, first_fingerprint2, remote_ip).should be_nil
|
||||||
MachineFingerprint.count.should eq(2)
|
MachineFingerprint.count.should eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -319,13 +366,22 @@ describe JamTrackRight do
|
||||||
|
|
||||||
it "let's you download a free jamtrack if you have a second but undownloaded free one" do
|
it "let's you download a free jamtrack if you have a second but undownloaded free one" do
|
||||||
right1 = FactoryGirl.create(:jam_track_right, user: user, redeemed: true, redeemed_and_fingerprinted: false)
|
right1 = FactoryGirl.create(:jam_track_right, user: user, redeemed: true, redeemed_and_fingerprinted: false)
|
||||||
|
first_fingerprint2 = first_fingerprint.clone
|
||||||
jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
|
jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
|
||||||
MachineFingerprint.count.should eq(2)
|
MachineFingerprint.count.should eq(2)
|
||||||
|
|
||||||
right1.guard_against_fraud(user, first_fingerprint, remote_ip).should eq('already redeemed another')
|
right1.guard_against_fraud(user, first_fingerprint2, remote_ip).should eq('already redeemed another')
|
||||||
MachineFingerprint.count.should eq(2)
|
MachineFingerprint.count.should eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "creates metadata" do
|
||||||
|
right1 = FactoryGirl.create(:jam_track_right, user: user, redeemed: true, redeemed_and_fingerprinted: false)
|
||||||
|
|
||||||
|
jam_track_right.guard_against_fraud(user, full_fingerprint, remote_ip).should be_nil
|
||||||
|
MachineFingerprint.count.should eq(2)
|
||||||
|
MachineExtra.count.should eq(2)
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,69 @@ describe Sale do
|
||||||
user.has_redeemable_jamtrack.should be_false
|
user.has_redeemable_jamtrack.should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "for a free jam track with an affiliate association" do
|
||||||
|
partner = FactoryGirl.create(:affiliate_partner)
|
||||||
|
user.affiliate_referral = partner
|
||||||
|
user.save!
|
||||||
|
|
||||||
|
shopping_cart = ShoppingCart.create user, jamtrack, 1, true
|
||||||
|
client.find_or_create_account(user, billing_info)
|
||||||
|
|
||||||
|
sales = Sale.place_order(user, [shopping_cart])
|
||||||
|
|
||||||
|
user.reload
|
||||||
|
user.sales.length.should eq(1)
|
||||||
|
|
||||||
|
sales.should eq(user.sales)
|
||||||
|
sale = sales[0]
|
||||||
|
sale.recurly_invoice_id.should be_nil
|
||||||
|
|
||||||
|
sale.recurly_subtotal_in_cents.should eq(0)
|
||||||
|
sale.recurly_tax_in_cents.should eq(0)
|
||||||
|
sale.recurly_total_in_cents.should eq(0)
|
||||||
|
sale.recurly_currency.should eq('USD')
|
||||||
|
sale.order_total.should eq(0)
|
||||||
|
sale.sale_line_items.length.should == 1
|
||||||
|
sale_line_item = sale.sale_line_items[0]
|
||||||
|
sale_line_item.recurly_tax_in_cents.should eq(0)
|
||||||
|
sale_line_item.recurly_total_in_cents.should eq(0)
|
||||||
|
sale_line_item.recurly_currency.should eq('USD')
|
||||||
|
sale_line_item.recurly_discount_in_cents.should eq(0)
|
||||||
|
sale_line_item.product_type.should eq(JamTrack::PRODUCT_TYPE)
|
||||||
|
sale_line_item.unit_price.should eq(jamtrack.price)
|
||||||
|
sale_line_item.quantity.should eq(1)
|
||||||
|
sale_line_item.free.should eq(1)
|
||||||
|
sale_line_item.sales_tax.should be_nil
|
||||||
|
sale_line_item.shipping_handling.should eq(0)
|
||||||
|
sale_line_item.recurly_plan_code.should eq(jamtrack.plan_code)
|
||||||
|
sale_line_item.product_id.should eq(jamtrack.id)
|
||||||
|
sale_line_item.recurly_subscription_uuid.should be_nil
|
||||||
|
sale_line_item.recurly_adjustment_uuid.should be_nil
|
||||||
|
sale_line_item.recurly_adjustment_credit_uuid.should be_nil
|
||||||
|
sale_line_item.recurly_adjustment_uuid.should eq(user.jam_track_rights.last.recurly_adjustment_uuid)
|
||||||
|
sale_line_item.recurly_adjustment_credit_uuid.should eq(user.jam_track_rights.last.recurly_adjustment_credit_uuid)
|
||||||
|
sale_line_item.affiliate_referral.should eq(partner)
|
||||||
|
sale_line_item.affiliate_referral_fee_in_cents.should eq(0)
|
||||||
|
|
||||||
|
# verify subscription is in Recurly
|
||||||
|
recurly_account = client.get_account(user)
|
||||||
|
adjustments = recurly_account.adjustments
|
||||||
|
adjustments.should_not be_nil
|
||||||
|
adjustments.should have(0).items
|
||||||
|
|
||||||
|
invoices = recurly_account.invoices
|
||||||
|
invoices.should have(0).items
|
||||||
|
|
||||||
|
|
||||||
|
# verify jam_track_rights data
|
||||||
|
user.jam_track_rights.should_not be_nil
|
||||||
|
user.jam_track_rights.should have(1).items
|
||||||
|
user.jam_track_rights.last.jam_track.id.should eq(jamtrack.id)
|
||||||
|
user.jam_track_rights.last.redeemed.should be_true
|
||||||
|
user.has_redeemable_jamtrack.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
it "for a normally priced jam track" do
|
it "for a normally priced jam track" do
|
||||||
user.has_redeemable_jamtrack = false
|
user.has_redeemable_jamtrack = false
|
||||||
user.save!
|
user.save!
|
||||||
|
|
@ -201,6 +264,85 @@ describe Sale do
|
||||||
user.jam_track_rights.last.jam_track.id.should eq(jamtrack.id)
|
user.jam_track_rights.last.jam_track.id.should eq(jamtrack.id)
|
||||||
user.jam_track_rights.last.redeemed.should be_false
|
user.jam_track_rights.last.redeemed.should be_false
|
||||||
user.has_redeemable_jamtrack.should be_false
|
user.has_redeemable_jamtrack.should be_false
|
||||||
|
|
||||||
|
sale_line_item.affiliate_referral.should be_nil
|
||||||
|
sale_line_item.affiliate_referral_fee_in_cents.should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "for a normally priced jam track with an affiliate association" do
|
||||||
|
user.has_redeemable_jamtrack = false
|
||||||
|
partner = FactoryGirl.create(:affiliate_partner)
|
||||||
|
user.affiliate_referral = partner
|
||||||
|
user.save!
|
||||||
|
shopping_cart = ShoppingCart.create user, jamtrack, 1, false
|
||||||
|
client.find_or_create_account(user, billing_info)
|
||||||
|
|
||||||
|
sales = Sale.place_order(user, [shopping_cart])
|
||||||
|
|
||||||
|
user.reload
|
||||||
|
user.sales.length.should eq(1)
|
||||||
|
|
||||||
|
sales.should eq(user.sales)
|
||||||
|
sale = sales[0]
|
||||||
|
sale.recurly_invoice_id.should_not be_nil
|
||||||
|
|
||||||
|
sale.recurly_subtotal_in_cents.should eq(jam_track_price_in_cents)
|
||||||
|
sale.recurly_tax_in_cents.should eq(0)
|
||||||
|
sale.recurly_total_in_cents.should eq(jam_track_price_in_cents)
|
||||||
|
sale.recurly_currency.should eq('USD')
|
||||||
|
|
||||||
|
sale.order_total.should eq(jamtrack.price)
|
||||||
|
sale.sale_line_items.length.should == 1
|
||||||
|
sale_line_item = sale.sale_line_items[0]
|
||||||
|
# validate we are storing pricing info from recurly
|
||||||
|
sale_line_item.recurly_tax_in_cents.should eq(0)
|
||||||
|
sale_line_item.recurly_total_in_cents.should eq(jam_track_price_in_cents)
|
||||||
|
sale_line_item.recurly_currency.should eq('USD')
|
||||||
|
sale_line_item.recurly_discount_in_cents.should eq(0)
|
||||||
|
sale_line_item.product_type.should eq(JamTrack::PRODUCT_TYPE)
|
||||||
|
sale_line_item.unit_price.should eq(jamtrack.price)
|
||||||
|
sale_line_item.quantity.should eq(1)
|
||||||
|
sale_line_item.free.should eq(0)
|
||||||
|
sale_line_item.sales_tax.should be_nil
|
||||||
|
sale_line_item.shipping_handling.should eq(0)
|
||||||
|
sale_line_item.recurly_plan_code.should eq(jamtrack.plan_code)
|
||||||
|
sale_line_item.product_id.should eq(jamtrack.id)
|
||||||
|
sale_line_item.recurly_subscription_uuid.should be_nil
|
||||||
|
sale_line_item.recurly_adjustment_uuid.should_not be_nil
|
||||||
|
sale_line_item.recurly_adjustment_credit_uuid.should be_nil
|
||||||
|
sale_line_item.recurly_adjustment_uuid.should eq(user.jam_track_rights.last.recurly_adjustment_uuid)
|
||||||
|
sale_line_item.affiliate_referral.should eq(partner)
|
||||||
|
sale_line_item.affiliate_referral_fee_in_cents.should eq(20)
|
||||||
|
|
||||||
|
# verify subscription is in Recurly
|
||||||
|
recurly_account = client.get_account(user)
|
||||||
|
adjustments = recurly_account.adjustments
|
||||||
|
adjustments.should_not be_nil
|
||||||
|
adjustments.should have(1).items
|
||||||
|
purchase= adjustments[0]
|
||||||
|
purchase.unit_amount_in_cents.should eq((jamtrack.price * 100).to_i)
|
||||||
|
purchase.accounting_code.should eq(ShoppingCart::PURCHASE_NORMAL)
|
||||||
|
purchase.description.should eq("JamTrack: " + jamtrack.name)
|
||||||
|
purchase.state.should eq('invoiced')
|
||||||
|
purchase.uuid.should eq(sale_line_item.recurly_adjustment_uuid)
|
||||||
|
|
||||||
|
invoices = recurly_account.invoices
|
||||||
|
invoices.should have(1).items
|
||||||
|
invoice = invoices[0]
|
||||||
|
invoice.uuid.should eq(sale.recurly_invoice_id)
|
||||||
|
invoice.line_items.should have(1).items # should have single adjustment associated
|
||||||
|
invoice.line_items[0].should eq(purchase)
|
||||||
|
invoice.subtotal_in_cents.should eq((jamtrack.price * 100).to_i)
|
||||||
|
invoice.total_in_cents.should eq((jamtrack.price * 100).to_i)
|
||||||
|
invoice.state.should eq('collected')
|
||||||
|
|
||||||
|
# verify jam_track_rights data
|
||||||
|
user.jam_track_rights.should_not be_nil
|
||||||
|
user.jam_track_rights.should have(1).items
|
||||||
|
user.jam_track_rights.last.jam_track.id.should eq(jamtrack.id)
|
||||||
|
user.jam_track_rights.last.redeemed.should be_false
|
||||||
|
user.has_redeemable_jamtrack.should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
it "for a jamtrack already owned" do
|
it "for a jamtrack already owned" do
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,14 @@ def app_config
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expire_fingerprint_days
|
||||||
|
14
|
||||||
|
end
|
||||||
|
|
||||||
|
def found_conflict_count
|
||||||
|
1
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def audiomixer_workspace_path
|
def audiomixer_workspace_path
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ else
|
||||||
gem 'jam_websockets', "0.1.#{ENV["BUILD_NUMBER"]}"
|
gem 'jam_websockets', "0.1.#{ENV["BUILD_NUMBER"]}"
|
||||||
ENV['NOKOGIRI_USE_SYSTEM_LIBRARIES'] ||= "true"
|
ENV['NOKOGIRI_USE_SYSTEM_LIBRARIES'] ||= "true"
|
||||||
end
|
end
|
||||||
|
|
||||||
gem 'oj', '2.10.2'
|
gem 'oj', '2.10.2'
|
||||||
gem 'builder'
|
gem 'builder'
|
||||||
gem 'rails', '~>3.2.11'
|
gem 'rails', '~>3.2.11'
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@ Jasmine Javascript Unit Tests
|
||||||
|
|
||||||
Open browser to localhost:3000/teaspoon
|
Open browser to localhost:3000/teaspoon
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 121 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -8,10 +8,10 @@
|
||||||
var rest = context.JK.Rest();
|
var rest = context.JK.Rest();
|
||||||
var userId;
|
var userId;
|
||||||
var user = {};
|
var user = {};
|
||||||
|
var screen = null;
|
||||||
var gearUtils = context.JK.GearUtilsInstance;
|
var gearUtils = context.JK.GearUtilsInstance;
|
||||||
|
|
||||||
function beforeShow(data) {
|
function beforeShow(data) {
|
||||||
console.log("beforeShow", data)
|
|
||||||
userId = data.id;
|
userId = data.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,8 +71,11 @@
|
||||||
invalidProfiles : invalidProfiles,
|
invalidProfiles : invalidProfiles,
|
||||||
isNativeClient: gon.isNativeClient,
|
isNativeClient: gon.isNativeClient,
|
||||||
musician: context.JK.currentUserMusician,
|
musician: context.JK.currentUserMusician,
|
||||||
webcamName: webcamName,
|
sales_count: userDetail.sales_count,
|
||||||
sales_count: userDetail.sales_count
|
is_affiliate_partner: userDetail.is_affiliate_partner,
|
||||||
|
affiliate_earnings: (userDetail.affiliate_earnings / 100).toFixed(2),
|
||||||
|
affiliate_referral_count: userDetail.affiliate_referral_count,
|
||||||
|
webcamName: webcamName
|
||||||
} , { variable: 'data' }));
|
} , { variable: 'data' }));
|
||||||
|
|
||||||
$('#account-content-scroller').html($template);
|
$('#account-content-scroller').html($template);
|
||||||
|
|
@ -135,6 +138,7 @@
|
||||||
$('#account-content-scroller').on('avatar_changed', '#profile-avatar', function(evt, newAvatarUrl) { evt.stopPropagation(); updateAvatar(newAvatarUrl); return false; })
|
$('#account-content-scroller').on('avatar_changed', '#profile-avatar', function(evt, newAvatarUrl) { evt.stopPropagation(); updateAvatar(newAvatarUrl); return false; })
|
||||||
|
|
||||||
$("#account-content-scroller").on('click', '#account-payment-history-link', function(evt) {evt.stopPropagation(); navToPaymentHistory(); return false; } );
|
$("#account-content-scroller").on('click', '#account-payment-history-link', function(evt) {evt.stopPropagation(); navToPaymentHistory(); return false; } );
|
||||||
|
$("#account-content-scroller").on('click', '#account-affiliate-partner-link', function(evt) {evt.stopPropagation(); navToAffiliates(); return false; } );
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderAccount() {
|
function renderAccount() {
|
||||||
|
|
@ -187,6 +191,11 @@
|
||||||
window.location = '/client#/account/paymentHistory'
|
window.location = '/client#/account/paymentHistory'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function navToAffiliates() {
|
||||||
|
resetForm()
|
||||||
|
window.location = '/client#/account/affiliatePartner'
|
||||||
|
}
|
||||||
|
|
||||||
// handle update avatar event
|
// handle update avatar event
|
||||||
function updateAvatar(avatar_url) {
|
function updateAvatar(avatar_url) {
|
||||||
var photoUrl = context.JK.resolveAvatarUrl(avatar_url);
|
var photoUrl = context.JK.resolveAvatarUrl(avatar_url);
|
||||||
|
|
@ -203,7 +212,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
|
screen = $('#account-content-scroller');
|
||||||
var screenBindings = {
|
var screenBindings = {
|
||||||
'beforeShow': beforeShow,
|
'beforeShow': beforeShow,
|
||||||
'afterShow': afterShow
|
'afterShow': afterShow
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,324 @@
|
||||||
|
(function (context, $) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
context.JK = context.JK || {};
|
||||||
|
context.JK.AccountAffiliateScreen = function (app) {
|
||||||
|
var logger = context.JK.logger;
|
||||||
|
var rest = context.JK.Rest();
|
||||||
|
var userId;
|
||||||
|
var user = {};
|
||||||
|
var affiliatePartnerTabs = ['account', 'agreement', 'signups', 'earnings'];
|
||||||
|
var affiliatePartnerData = null;
|
||||||
|
var $screen = null;
|
||||||
|
|
||||||
|
function beforeShow(data) {
|
||||||
|
userId = data.id;
|
||||||
|
affiliatePartnerData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function afterShow(data) {
|
||||||
|
|
||||||
|
rest.getAffiliatePartnerData(userId)
|
||||||
|
.done(function (response) {
|
||||||
|
affiliatePartnerData = response;
|
||||||
|
renderAffiliateTab('account')
|
||||||
|
})
|
||||||
|
.fail(app.ajaxError)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function events() {
|
||||||
|
|
||||||
|
// Affiliate Partner
|
||||||
|
$("#account-affiliate-partner-content-scroller").on('click', '#affiliate-partner-account-link', function (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
renderAffiliateTab('account');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$("#account-affiliate-partner-content-scroller").on('click', '#affiliate-partner-links-link', function (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
renderAffiliateTab('links');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$("#account-affiliate-partner-content-scroller").on('click', '#affiliate-partner-agreement-link', function (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
renderAffiliateTab('agreement');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$("#account-affiliate-partner-content-scroller").on('click', '#affiliate-partner-signups-link', function (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
renderAffiliateTab('signups');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$("#account-affiliate-partner-content-scroller").on('click', '#affiliate-partner-earnings-link', function (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
renderAffiliateTab('earnings');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$("#account-affiliate-partner-content-scroller").on('click', '#affiliate-profile-account-submit', function (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
handleUpdateAffiliateAccount();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _renderAffiliateTableSignups(rows) {
|
||||||
|
rest.getAffiliateSignups()
|
||||||
|
.done(onAffiliateSignups)
|
||||||
|
.fail(app.ajaxError)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _renderAffiliateTableEarnings(rows) {
|
||||||
|
rest.getAffiliatePayments()
|
||||||
|
.done(onAffiliatePayments)
|
||||||
|
.fail(app.ajaxError)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _renderAffiliateTableLinks(rows) {
|
||||||
|
$screen.find('.affiliate-agreement').on('click', function () {
|
||||||
|
renderAffiliateTab('agreement');
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
|
||||||
|
$screen.find('.affiliate-link-page').attr('href', '/affiliate/links/' + affiliatePartnerData.account.id)
|
||||||
|
|
||||||
|
$screen.find('select.link_type').easyDropDown();
|
||||||
|
|
||||||
|
var $linkType = $screen.find('.link_type')
|
||||||
|
|
||||||
|
$linkType.on('change', function() {
|
||||||
|
logger.debug("link type changed")
|
||||||
|
updateLinks();
|
||||||
|
})
|
||||||
|
|
||||||
|
updateLinks();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAffiliateSignups(signups) {
|
||||||
|
|
||||||
|
var $table = $screen.find('table.traffic-table tbody')
|
||||||
|
$table.empty();
|
||||||
|
|
||||||
|
var template = $('#template-affiliate-partner-signups-row').html();
|
||||||
|
context._.each(signups.traffics, function(item) {
|
||||||
|
var $link = $(context._.template(template, item, {variable: 'data'}));
|
||||||
|
|
||||||
|
var $day = $link.find('td.day')
|
||||||
|
|
||||||
|
var day = $day.text();
|
||||||
|
var bits = day.split('-')
|
||||||
|
if(bits.length == 3) {
|
||||||
|
$day.text(context.JK.getMonth(new Number(bits[1]) - 1) + ' ' + new Number(bits[2]))
|
||||||
|
}
|
||||||
|
|
||||||
|
$table.append($link)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAffiliatePayments(payments) {
|
||||||
|
var $table = $screen.find('table.payment-table tbody')
|
||||||
|
$table.empty();
|
||||||
|
|
||||||
|
var template = $('#template-affiliate-partner-earnings-row').html();
|
||||||
|
context._.each(payments.payments, function(item) {
|
||||||
|
|
||||||
|
var data = {}
|
||||||
|
if(item.payment_type == 'quarterly') {
|
||||||
|
|
||||||
|
if(item.quarter == 0) {
|
||||||
|
data.time = '1st Quarter ' + item.year
|
||||||
|
}
|
||||||
|
else if(item.quarter == 1) {
|
||||||
|
data.time = '2nd Quarter ' + item.year
|
||||||
|
}
|
||||||
|
else if(item.quarter == 2) {
|
||||||
|
data.time = '3rd Quarter ' + item.year
|
||||||
|
}
|
||||||
|
else if(item.quarter == 3) {
|
||||||
|
data.time = '4th Quarter ' + item.year
|
||||||
|
}
|
||||||
|
|
||||||
|
data.sold = ''
|
||||||
|
|
||||||
|
if(item.paid) {
|
||||||
|
data.earnings = 'PAID $' + (item.due_amount_in_cents / 100).toFixed(2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data.earnings = 'No earning were paid, as the $10 minimum threshold was not reached.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data.time = context.JK.getMonth(item.month - 1) + ' ' + item.year;
|
||||||
|
if(item.jamtracks_sold == 1) {
|
||||||
|
data.sold = 'JamTracks: ' + item.jamtracks_sold + ' unit sold';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data.sold = 'JamTracks: ' + item.jamtracks_sold + ' units sold';
|
||||||
|
}
|
||||||
|
data.earnings = '$' + (item.due_amount_in_cents / 100).toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var $earning = $(context._.template(template, data, {variable: 'data'}));
|
||||||
|
|
||||||
|
$table.append($earning)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateLinks() {
|
||||||
|
var $select = $screen.find('select.link_type')
|
||||||
|
var value = $select.val()
|
||||||
|
|
||||||
|
logger.debug("value: " + value)
|
||||||
|
|
||||||
|
var type = 'jamtrack_songs';
|
||||||
|
if(value == 'JamTrack Song') {
|
||||||
|
type = 'jamtrack_songs'
|
||||||
|
}
|
||||||
|
else if(value == 'JamTrack Band') {
|
||||||
|
type = 'jamtrack_bands'
|
||||||
|
}
|
||||||
|
else if(value == 'JamTrack General') {
|
||||||
|
type = 'jamtrack_general'
|
||||||
|
}
|
||||||
|
else if(value == 'JamKazam General') {
|
||||||
|
type = 'jamkazam'
|
||||||
|
}
|
||||||
|
else if(value == 'JamKazam Session') {
|
||||||
|
type = 'sessions'
|
||||||
|
}
|
||||||
|
else if(value == 'JamKazam Recording') {
|
||||||
|
type = 'recordings'
|
||||||
|
}
|
||||||
|
else if(value == 'Custom Link') {
|
||||||
|
type = 'custom_links'
|
||||||
|
}
|
||||||
|
|
||||||
|
$screen.find('.link-type-prompt').hide();
|
||||||
|
$screen.find('.link-type-prompt[data-type="' + type + '"]').show();
|
||||||
|
|
||||||
|
if(type == 'custom_links') {
|
||||||
|
$screen.find('table.links-table').hide();
|
||||||
|
$screen.find('.link-type-prompt[data-type="custom_links"] span.affiliate_id').text(affiliatePartnerData.account.id)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rest.getLinks(type)
|
||||||
|
.done(populateLinkTable)
|
||||||
|
.fail(function() {
|
||||||
|
app.notify({message: 'Unable to fetch links. Please try again later.' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _renderAffiliateTab(theTab) {
|
||||||
|
affiliateTabRefresh(theTab);
|
||||||
|
var template = $('#template-affiliate-partner-' + theTab).html();
|
||||||
|
var tabHtml = context._.template(template, affiliatePartnerData[theTab], {variable: 'data'});
|
||||||
|
$('#affiliate-partner-tab-content').html(tabHtml);
|
||||||
|
|
||||||
|
if (theTab == 'signups') {
|
||||||
|
_renderAffiliateTableSignups(affiliatePartnerData[theTab]);
|
||||||
|
} else if (theTab == 'earnings') {
|
||||||
|
_renderAffiliateTableEarnings(affiliatePartnerData[theTab]);
|
||||||
|
} else if (theTab == 'links') {
|
||||||
|
_renderAffiliateTableLinks(affiliatePartnerData[theTab]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAffiliateTab(theTab) {
|
||||||
|
if (affiliatePartnerData) {
|
||||||
|
return _renderAffiliateTab(theTab);
|
||||||
|
}
|
||||||
|
rest.getAffiliatePartnerData(userId)
|
||||||
|
.done(function (response) {
|
||||||
|
affiliatePartnerData = response;
|
||||||
|
_renderAffiliateTab(theTab);
|
||||||
|
})
|
||||||
|
.fail(app.ajaxError)
|
||||||
|
}
|
||||||
|
|
||||||
|
function affiliateTabRefresh(selectedTab) {
|
||||||
|
var container = $('#account-affiliate-partner-content-scroller');
|
||||||
|
container.find('.affiliate-partner-nav a.active').removeClass('active');
|
||||||
|
if (selectedTab) {
|
||||||
|
container.find('.affiliate-partner-' + selectedTab).show();
|
||||||
|
$.each(affiliatePartnerTabs, function (index, val) {
|
||||||
|
if (val != selectedTab) {
|
||||||
|
container.find('.affiliate-partner-' + val).hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
container.find('.affiliate-partner-nav a#affiliate-partner-' + selectedTab + '-link').addClass('active');
|
||||||
|
} else {
|
||||||
|
$.each(affiliatePartnerTabs, function (index, val) {
|
||||||
|
container.find('.affiliate-partner-' + val).hide();
|
||||||
|
});
|
||||||
|
container.find('.affiliate-partner-nav a#affiliate-partner-' + affiliatePartnerTabs[0] + '-link').addClass('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdateAffiliateAccount() {
|
||||||
|
var tab_content = $('#affiliate-partner-tab-content');
|
||||||
|
var affiliate_partner_data = {
|
||||||
|
'address': {
|
||||||
|
'address1': tab_content.find('#affiliate_partner_address1').val(),
|
||||||
|
'address2': tab_content.find('#affiliate_partner_address2').val(),
|
||||||
|
'city': tab_content.find('#affiliate_partner_city').val(),
|
||||||
|
'state': tab_content.find('#affiliate_partner_state').val(),
|
||||||
|
'postal_code': tab_content.find('#affiliate_partner_postal_code').val(),
|
||||||
|
'country': tab_content.find('#affiliate_partner_country').val()
|
||||||
|
},
|
||||||
|
'tax_identifier': tab_content.find('#affiliate_partner_tax_identifier').val()
|
||||||
|
}
|
||||||
|
rest.postAffiliatePartnerData(userId, affiliate_partner_data)
|
||||||
|
.done(postUpdateAffiliateAccountSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
function postUpdateAffiliateAccountSuccess(response) {
|
||||||
|
app.notify(
|
||||||
|
{
|
||||||
|
title: "Affiliate Account",
|
||||||
|
text: "You have updated your affiliate partner data successfully."
|
||||||
|
},
|
||||||
|
null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateLinkTable(response) {
|
||||||
|
$screen.find('table.links-table').show();
|
||||||
|
var $linkTable = $screen.find('.links-table tbody')
|
||||||
|
|
||||||
|
$linkTable.empty();
|
||||||
|
var template = $('#template-affiliate-link-entry').html();
|
||||||
|
context._.each(response, function(item) {
|
||||||
|
var $link = $(context._.template(template, item, {variable: 'data'}));
|
||||||
|
$link.find('td.copy-link a').click(copyLink)
|
||||||
|
$linkTable.append($link)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyLink() {
|
||||||
|
var element = $(this);
|
||||||
|
var $url = element.closest('tr').find('td.url input')
|
||||||
|
$url.select()
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
var screenBindings = {
|
||||||
|
'beforeShow': beforeShow,
|
||||||
|
'afterShow': afterShow
|
||||||
|
};
|
||||||
|
app.bindScreen('account/affiliatePartner', screenBindings);
|
||||||
|
$screen = $('#account-affiliate-partner')
|
||||||
|
events();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initialize = initialize;
|
||||||
|
this.beforeShow = beforeShow;
|
||||||
|
this.afterShow = afterShow;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
})(window, jQuery);
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
||||||
// GO AFTER THE REQUIRES BELOW.
|
// GO AFTER THE REQUIRES BELOW.
|
||||||
//
|
//
|
||||||
|
//= require bugsnag
|
||||||
//= require bind-polyfill
|
//= require bind-polyfill
|
||||||
//= require jquery
|
//= require jquery
|
||||||
//= require jquery.monkeypatch
|
//= require jquery.monkeypatch
|
||||||
|
|
@ -36,6 +37,7 @@
|
||||||
//= require jquery.custom-protocol
|
//= require jquery.custom-protocol
|
||||||
//= require jquery.exists
|
//= require jquery.exists
|
||||||
//= require jquery.payment
|
//= require jquery.payment
|
||||||
|
//= require jquery.visible
|
||||||
//= require howler.core.js
|
//= require howler.core.js
|
||||||
//= require jstz
|
//= require jstz
|
||||||
//= require class
|
//= require class
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@
|
||||||
displayTax(effectiveQuantity, unitTax, 1.99 + unitTax)
|
displayTax(effectiveQuantity, unitTax, 1.99 + unitTax)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
checkoutUtils.configureRecurly()
|
||||||
var pricing = context.recurly.Pricing();
|
var pricing = context.recurly.Pricing();
|
||||||
pricing.plan_code = gon.recurly_tax_estimate_jam_track_plan;
|
pricing.plan_code = gon.recurly_tax_estimate_jam_track_plan;
|
||||||
pricing.resolved = false;
|
pricing.resolved = false;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ class CheckoutUtils
|
||||||
@rest = new context.JK.Rest();
|
@rest = new context.JK.Rest();
|
||||||
@cookie_name = "preserve_billing"
|
@cookie_name = "preserve_billing"
|
||||||
@lastPurchaseResponse = null
|
@lastPurchaseResponse = null
|
||||||
|
@configuredRecurly = false
|
||||||
|
|
||||||
init: () =>
|
init: () =>
|
||||||
|
|
||||||
|
|
@ -54,5 +55,10 @@ class CheckoutUtils
|
||||||
|
|
||||||
return carts[0].product_info.free
|
return carts[0].product_info.free
|
||||||
|
|
||||||
|
configureRecurly: () =>
|
||||||
|
unless @configuredRecurly
|
||||||
|
context.recurly.configure(gon.global.recurly_public_api_key)
|
||||||
|
@configuredRecurly = true
|
||||||
|
|
||||||
# global instance
|
# global instance
|
||||||
context.JK.CheckoutUtilsInstance = new CheckoutUtils()
|
context.JK.CheckoutUtilsInstance = new CheckoutUtils()
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//= require bugsnag
|
||||||
//= require jquery
|
//= require jquery
|
||||||
//= require jquery.queryparams
|
//= require jquery.queryparams
|
||||||
//= require AAA_Log
|
//= require AAA_Log
|
||||||
|
|
|
||||||
|
|
@ -133,20 +133,25 @@
|
||||||
if(options.buttons) {
|
if(options.buttons) {
|
||||||
context._.each(options.buttons, function(button, i) {
|
context._.each(options.buttons, function(button, i) {
|
||||||
if(!button.name) throw "button.name must be specified";
|
if(!button.name) throw "button.name must be specified";
|
||||||
if(!button.click) throw "button.click must be specified";
|
if(!button.click && !button.href) throw "button.click or button.href must be specified";
|
||||||
|
|
||||||
var buttonStyle = button.buttonStyle;
|
var buttonStyle = button.buttonStyle;
|
||||||
if(!buttonStyle) {
|
if(!buttonStyle) {
|
||||||
buttonStyle = options.buttons.length == i + 1 ? 'button-orange' : 'button-grey';
|
buttonStyle = options.buttons.length == i + 1 ? 'button-orange' : 'button-grey';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var $btn = $('<a class="' + buttonStyle + ' user-btn">' + button.name + '</a>');
|
var $btn = $('<a class="' + buttonStyle + ' user-btn">' + button.name + '</a>');
|
||||||
|
|
||||||
|
if(button.href) {
|
||||||
|
$btn.attr('href', button.href)
|
||||||
|
}
|
||||||
|
else {
|
||||||
$btn.click(function() {
|
$btn.click(function() {
|
||||||
button.click();
|
button.click();
|
||||||
hide();
|
hide();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
$buttons.append($btn);
|
$buttons.append($btn);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,11 @@
|
||||||
var stun = null;
|
var stun = null;
|
||||||
var rest = context.JK.Rest();
|
var rest = context.JK.Rest();
|
||||||
|
|
||||||
|
if(gon.global.web_performance_timing_enabled) {
|
||||||
|
$(window).on('load', sendTimingResults)
|
||||||
|
$(window).on('pagehide', setNavigationStart)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$(document).on('JAMKAZAM_CONSTRUCTED', function(e, data) {
|
$(document).on('JAMKAZAM_CONSTRUCTED', function(e, data) {
|
||||||
|
|
||||||
|
|
@ -245,4 +250,63 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setNavigationStart() {
|
||||||
|
if(!window.performance || !window.performance.timing) {
|
||||||
|
try {
|
||||||
|
window.sessionStorage.setItem('navigationStart', Date.now())
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
logger.debug("unable to accesss sessionStorage")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// http://githubengineering.com/browser-monitoring-for-github-com/
|
||||||
|
function sendTimingResults() {
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
var timing;
|
||||||
|
var hasTimingApi;
|
||||||
|
|
||||||
|
if(window.performance && window.performance.timing) {
|
||||||
|
timing = window.performance.timing
|
||||||
|
hasTimingApi = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timing = {}
|
||||||
|
hasTimingApi = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge in simulated cross-browser load event
|
||||||
|
timing['crossBrowserLoadEvent'] = Date.now()
|
||||||
|
|
||||||
|
var chromeFirstPaintTime;
|
||||||
|
if(window.chrome && window.chrome.loadTimes) {
|
||||||
|
var loadTimes = window.chrome.loadTimes()
|
||||||
|
if(loadTimes) {
|
||||||
|
chromeFirstPaintTime = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// firstPaintTime is in seconds; convert to milliseconds to match performance.timing.
|
||||||
|
timing['chromeFirstPaintTime'] = Math.round(chromeFirstPaintTime * 1000);
|
||||||
|
|
||||||
|
// Merge in simulated navigation start, if no navigation timing
|
||||||
|
if (!hasTimingApi) {
|
||||||
|
try {
|
||||||
|
var navStart = window.sessionStorage.getItem('navigationStart')
|
||||||
|
if(navStart) {
|
||||||
|
timing['navigationStart'] = parseInt(navStart, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
context.JK.GA.trackTiming(timing);
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
})(window, jQuery);
|
})(window, jQuery);
|
||||||
|
|
|
||||||
|
|
@ -284,6 +284,37 @@
|
||||||
logger.debug("Tracked Jam Track Play")
|
logger.debug("Tracked Jam Track Play")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trackTiming(timing) {
|
||||||
|
|
||||||
|
if(!timing) {return}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var computed = {
|
||||||
|
dns: timing.domainLookupEnd - timing.domainLookupStart,
|
||||||
|
connect: timing.connectEnd - timing.connectStart,
|
||||||
|
ttfb: timing.responseStart - timing.connectEnd,
|
||||||
|
basePage: timing.responseEnd - timing.responseStart,
|
||||||
|
frontEnd: timing.loadEventStart - timing.responseEnd,
|
||||||
|
domContentLoadedEvent: timing.domContentLoadedEventEnd - timing.domContentLoadedEventStart,
|
||||||
|
windowLoadEvent: timing.loadEventEnd - timing.loadEventStart,
|
||||||
|
domInteractive: timing.domInteractive - timing.domLoading,
|
||||||
|
domComplete: timing.domComplete - timing.domLoading,
|
||||||
|
domCompleteToOnload: timing.loadEventStart - timing.domComplete
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.debug("page load time: " + computed.frontEnd)
|
||||||
|
context._.each(computed, function (value, key) {
|
||||||
|
if (value > 0 && value < 60000) {
|
||||||
|
context.ga("send", "timing", "NavigationTiming", key, value, null, {'page' : '/' + window.location.pathname});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//context.stats.write('web.timing.navigation', computed)
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
logger.error("loading times failed in ga.js", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if you want to pass in no title, either omit it from the arg list when u invoke virtualPageView, or pass in undefined, NOT null
|
// if you want to pass in no title, either omit it from the arg list when u invoke virtualPageView, or pass in undefined, NOT null
|
||||||
function virtualPageView(page, title) {
|
function virtualPageView(page, title) {
|
||||||
|
|
||||||
|
|
@ -445,6 +476,7 @@
|
||||||
GA.trackBand = trackBand;
|
GA.trackBand = trackBand;
|
||||||
GA.trackJKSocial = trackJKSocial;
|
GA.trackJKSocial = trackJKSocial;
|
||||||
GA.virtualPageView = virtualPageView;
|
GA.virtualPageView = virtualPageView;
|
||||||
|
GA.trackTiming = trackTiming;
|
||||||
|
|
||||||
context.JK.GA = GA;
|
context.JK.GA = GA;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@
|
||||||
JAMTRACK_DOWNLOADER_STATE_CHANGED: 'jamtrack_downloader_state',
|
JAMTRACK_DOWNLOADER_STATE_CHANGED: 'jamtrack_downloader_state',
|
||||||
METRONOME_PLAYBACK_MODE_SELECTED: 'metronome_playback_mode_selected',
|
METRONOME_PLAYBACK_MODE_SELECTED: 'metronome_playback_mode_selected',
|
||||||
CHECKOUT_SIGNED_IN: 'checkout_signed_in',
|
CHECKOUT_SIGNED_IN: 'checkout_signed_in',
|
||||||
CHECKOUT_SKIP_SIGN_IN: 'checkout_skip_sign_in'
|
CHECKOUT_SKIP_SIGN_IN: 'checkout_skip_sign_in',
|
||||||
|
PREVIEW_PLAYED: 'preview_played'
|
||||||
};
|
};
|
||||||
|
|
||||||
context.JK.PLAYBACK_MONITOR_MODE = {
|
context.JK.PLAYBACK_MONITOR_MODE = {
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
helpBubble.rotateJamTrackLandingBubbles = function($preview, $video, $ctaButton, $alternativeCta) {
|
||||||
|
$(window).on('load', function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
helpBubble.jamtrackLandingPreview($preview, $preview.offsetParent())
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
helpBubble.jamtrackLandingVideo($video, $video.offsetParent())
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
helpBubble.jamtrackLandingCta($ctaButton, $alternativeCta)
|
||||||
|
}, 11000); // 5 seconds on top of 6 second show time of bubbles
|
||||||
|
}, 11000); // 5 seconds on top of 6 second show time of bubbles
|
||||||
|
}, 1500)
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
helpBubble.clearJamTrackGuide = clearJamTrackGuideTimeout;
|
helpBubble.clearJamTrackGuide = clearJamTrackGuideTimeout;
|
||||||
|
|
||||||
helpBubble.jamtrackGuideTile = function ($element, $offsetParent) {
|
helpBubble.jamtrackGuideTile = function ($element, $offsetParent) {
|
||||||
|
|
@ -82,4 +100,33 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
helpBubble.jamtrackLandingPreview = function($element, $offsetParent) {
|
||||||
|
context.JK.prodBubble($element, 'jamtrack-landing-preview', {}, bigHelpOptions({positions:['right'], offsetParent: $offsetParent}))
|
||||||
|
}
|
||||||
|
|
||||||
|
helpBubble.jamtrackLandingVideo = function($element, $offsetParent) {
|
||||||
|
context.JK.prodBubble($element, 'jamtrack-landing-video', {}, bigHelpOptions({positions:['left'], offsetParent: $offsetParent}))
|
||||||
|
}
|
||||||
|
|
||||||
|
helpBubble.jamtrackLandingCta = function($element, $alternativeElement) {
|
||||||
|
if ($element.visible()) {
|
||||||
|
context.JK.prodBubble($element, 'jamtrack-landing-cta', {}, bigHelpOptions({positions:['left']}))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.JK.prodBubble($alternativeElement, 'jamtrack-landing-cta', {}, bigHelpOptions({positions:['right']}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
helpBubble.jamtrackBrowseBand = function($element, $offsetParent) {
|
||||||
|
return context.JK.prodBubble($element, 'jamtrack-browse-band', {}, bigHelpOptions({positions:['top'], offsetParent: $offsetParent}))
|
||||||
|
}
|
||||||
|
|
||||||
|
helpBubble.jamtrackBrowseMasterMix = function($element, $offsetParent) {
|
||||||
|
return context.JK.prodBubble($element, 'jamtrack-browse-master-mix', {}, bigHelpOptions({positions:['top'], offsetParent: $offsetParent}))
|
||||||
|
}
|
||||||
|
|
||||||
|
helpBubble.jamtrackBrowseCta = function($element, $offsetParent) {
|
||||||
|
return context.JK.prodBubble($element, 'jamtrack-browse-cta', {}, bigHelpOptions({positions:['top'], offsetParent: $offsetParent}))
|
||||||
|
}
|
||||||
|
|
||||||
})(window, jQuery);
|
})(window, jQuery);
|
||||||
|
|
@ -499,6 +499,80 @@
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createAffiliatePartner(options) {
|
||||||
|
return $.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: '/api/affiliate_partners',
|
||||||
|
dataType: "json",
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(options)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAffiliatePartnerData(userId) {
|
||||||
|
return $.ajax({
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
url: "/api/users/"+userId+"/affiliate_partner"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function postAffiliatePartnerData(userId, data) {
|
||||||
|
return $.ajax({
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
url: "/api/users/"+userId+"/affiliate_partner",
|
||||||
|
contentType: 'application/json',
|
||||||
|
processData:false,
|
||||||
|
data: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLinks(type, partner_id) {
|
||||||
|
var url = "/api/links/" + type;
|
||||||
|
|
||||||
|
if(partner_id) {
|
||||||
|
url += '?affiliate_id=' + partner_id;
|
||||||
|
}
|
||||||
|
return $.ajax({
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAffiliateSignups() {
|
||||||
|
return $.ajax({
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
url: "/api/affiliate_partners/signups"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAffiliateMonthly() {
|
||||||
|
return $.ajax({
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
url: "/api/affiliate_partners/monthly_earnings"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAffiliateQuarterly() {
|
||||||
|
return $.ajax({
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
url: "/api/affiliate_partners/quarterly_earnings"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAffiliatePayments() {
|
||||||
|
return $.ajax({
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
url: "/api/affiliate_partners/payments"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getCities(options) {
|
function getCities(options) {
|
||||||
var country = options['country']
|
var country = options['country']
|
||||||
var region = options['region']
|
var region = options['region']
|
||||||
|
|
@ -1492,12 +1566,13 @@
|
||||||
|
|
||||||
function enqueueJamTrack(options) {
|
function enqueueJamTrack(options) {
|
||||||
var jamTrackId = options['id'];
|
var jamTrackId = options['id'];
|
||||||
|
delete options['id']
|
||||||
|
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
url: '/api/jamtracks/enqueue/' + jamTrackId + '?' + $.param(options),
|
url: '/api/jamtracks/enqueue/' + jamTrackId,
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
contentType: 'applications/json'
|
data: options
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1738,6 +1813,14 @@
|
||||||
this.updateScheduledSession = updateScheduledSession;
|
this.updateScheduledSession = updateScheduledSession;
|
||||||
this.getUserDetail = getUserDetail;
|
this.getUserDetail = getUserDetail;
|
||||||
this.getUserProfile = getUserProfile;
|
this.getUserProfile = getUserProfile;
|
||||||
|
this.getAffiliatePartnerData = getAffiliatePartnerData;
|
||||||
|
this.postAffiliatePartnerData = postAffiliatePartnerData;
|
||||||
|
this.createAffiliatePartner = createAffiliatePartner;
|
||||||
|
this.getLinks = getLinks;
|
||||||
|
this.getAffiliateSignups = getAffiliateSignups;
|
||||||
|
this.getAffiliateMonthly = getAffiliateMonthly;
|
||||||
|
this.getAffiliateQuarterly = getAffiliateQuarterly;
|
||||||
|
this.getAffiliatePayments = getAffiliatePayments;
|
||||||
this.getCities = getCities;
|
this.getCities = getCities;
|
||||||
this.getRegions = getRegions;
|
this.getRegions = getRegions;
|
||||||
this.getCountries = getCountries;
|
this.getCountries = getCountries;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
||||||
@EVENTS = context.JK.EVENTS
|
@EVENTS = context.JK.EVENTS
|
||||||
@rest = context.JK.Rest()
|
@rest = context.JK.Rest()
|
||||||
@logger = context.JK.logger
|
@logger = context.JK.logger
|
||||||
@options = options || {master_shows_duration: false, color:'gray', add_line_break: false}
|
@options = options || {master_shows_duration: false, color:'gray', add_line_break: false, preload_master:false}
|
||||||
@app = app
|
@app = app
|
||||||
@jamTrack = jamTrack
|
@jamTrack = jamTrack
|
||||||
@jamTrackTrack = jamTrackTrack
|
@jamTrackTrack = jamTrackTrack
|
||||||
|
|
@ -19,7 +19,9 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
||||||
@instrumentIcon = null
|
@instrumentIcon = null
|
||||||
@instrumentName = null
|
@instrumentName = null
|
||||||
@part = null
|
@part = null
|
||||||
|
@loaded = false
|
||||||
@loading = null
|
@loading = null
|
||||||
|
@loadingText = null
|
||||||
|
|
||||||
template = $('#template-jam-track-preview')
|
template = $('#template-jam-track-preview')
|
||||||
throw "no jam track preview template" if not template.exists()
|
throw "no jam track preview template" if not template.exists()
|
||||||
|
|
@ -31,6 +33,7 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
||||||
@instrumentName = @root.find('.instrument-name')
|
@instrumentName = @root.find('.instrument-name')
|
||||||
@part = @root.find('.part')
|
@part = @root.find('.part')
|
||||||
@loading = @root.find('.loading')
|
@loading = @root.find('.loading')
|
||||||
|
@loadingText = @root.find('.loading-text')
|
||||||
|
|
||||||
@playButton.on('click', @play)
|
@playButton.on('click', @play)
|
||||||
@stopButton.on('click', @stop)
|
@stopButton.on('click', @stop)
|
||||||
|
|
@ -91,6 +94,16 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
||||||
if @no_audio
|
if @no_audio
|
||||||
@playButton.addClass('disabled')
|
@playButton.addClass('disabled')
|
||||||
@stopButton.addClass('disabled')
|
@stopButton.addClass('disabled')
|
||||||
|
else
|
||||||
|
if @options.preload_master && @jamTrackTrack.track_type == 'Master'
|
||||||
|
@sound = new Howl({
|
||||||
|
src: @urls,
|
||||||
|
autoplay: false,
|
||||||
|
loop: false,
|
||||||
|
volume: 1.0,
|
||||||
|
preload: true,
|
||||||
|
onload: @onHowlerLoad
|
||||||
|
onend: @onHowlerEnd})
|
||||||
|
|
||||||
onDestroyed: () =>
|
onDestroyed: () =>
|
||||||
@sound.unload()
|
@sound.unload()
|
||||||
|
|
@ -108,12 +121,16 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
||||||
@removeNowPlaying()
|
@removeNowPlaying()
|
||||||
|
|
||||||
onHowlerLoad: () =>
|
onHowlerLoad: () =>
|
||||||
@loading.addClass('hidden')
|
@loaded = true
|
||||||
|
@loading.fadeOut();
|
||||||
|
@loadingText.fadeOut(); #addClass('hidden')
|
||||||
|
|
||||||
play: (e) =>
|
play: (e) =>
|
||||||
if e?
|
if e?
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
|
$(this).triggerHandler(@EVENTS.PREVIEW_PLAYED)
|
||||||
|
|
||||||
if @no_audio
|
if @no_audio
|
||||||
context.JK.prodBubble(@playButton, 'There is no preview available for this track.', {}, {duration:2000})
|
context.JK.prodBubble(@playButton, 'There is no preview available for this track.', {}, {duration:2000})
|
||||||
else
|
else
|
||||||
|
|
@ -129,7 +146,9 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
||||||
onload: @onHowlerLoad
|
onload: @onHowlerLoad
|
||||||
onend: @onHowlerEnd})
|
onend: @onHowlerEnd})
|
||||||
|
|
||||||
|
unless @loaded
|
||||||
@loading.removeClass('hidden')
|
@loading.removeClass('hidden')
|
||||||
|
@loadingText.removeClass('hidden')
|
||||||
|
|
||||||
|
|
||||||
@logger.debug("play issued for jam track preview")
|
@logger.debug("play issued for jam track preview")
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ context.JK.JamTrackScreen=class JamTrackScreen
|
||||||
instrument_logo_map = context.JK.getInstrumentIconMap24()
|
instrument_logo_map = context.JK.getInstrumentIconMap24()
|
||||||
|
|
||||||
constructor: (@app) ->
|
constructor: (@app) ->
|
||||||
|
@EVENTS = context.JK.EVENTS
|
||||||
@logger = context.JK.logger
|
@logger = context.JK.logger
|
||||||
@screen = null
|
@screen = null
|
||||||
@content = null
|
@content = null
|
||||||
|
|
@ -21,6 +22,7 @@ context.JK.JamTrackScreen=class JamTrackScreen
|
||||||
@next = null
|
@next = null
|
||||||
@currentQuery = this.defaultQuery()
|
@currentQuery = this.defaultQuery()
|
||||||
@expanded = null
|
@expanded = null
|
||||||
|
@shownHelperBubbles = false
|
||||||
|
|
||||||
beforeShow:(data) =>
|
beforeShow:(data) =>
|
||||||
this.setFilterFromURL()
|
this.setFilterFromURL()
|
||||||
|
|
@ -38,11 +40,17 @@ context.JK.JamTrackScreen=class JamTrackScreen
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
this.refresh()
|
this.refresh()
|
||||||
|
unless @shownHelperBubbles
|
||||||
|
@shownHelperBubbles = true
|
||||||
|
@startHelperBubbles()
|
||||||
|
|
||||||
afterShow:(data) =>
|
afterShow:(data) =>
|
||||||
context.JK.Tracking.jamtrackBrowseTrack(@app)
|
context.JK.Tracking.jamtrackBrowseTrack(@app)
|
||||||
|
|
||||||
beforeHide: () =>
|
beforeHide: () =>
|
||||||
|
this.clearCtaHelpTimeout()
|
||||||
|
this.clearBandFilterHelpTimeout()
|
||||||
|
this.clearMasterHelpTimeout()
|
||||||
this.clearResults();
|
this.clearResults();
|
||||||
|
|
||||||
events:() =>
|
events:() =>
|
||||||
|
|
@ -57,6 +65,104 @@ context.JK.JamTrackScreen=class JamTrackScreen
|
||||||
@noMoreJamtracks.hide()
|
@noMoreJamtracks.hide()
|
||||||
@next = null
|
@next = null
|
||||||
|
|
||||||
|
startHelperBubbles: () =>
|
||||||
|
@showBandFilterHelpTimeout = setTimeout(@showBandFilterHelp, 3500)
|
||||||
|
|
||||||
|
showBandFilterHelp: () =>
|
||||||
|
context.JK.HelpBubbleHelper.jamtrackBrowseBand(@artist.closest('.easydropdown-wrapper'), $('body'))
|
||||||
|
|
||||||
|
@showMasterHelpDueTime = new Date().getTime() + 11000 # 6000 ms for band tooltip to display, and 5 seconds of quiet time
|
||||||
|
@scroller.on('scroll', @masterHelpScrollWatch)
|
||||||
|
@scroller.on('scroll', @clearBubbles)
|
||||||
|
@showMasterHelpTimeout = setTimeout(@showMasterHelp, @masterHelpDueTime())
|
||||||
|
|
||||||
|
clearBubbles: () =>
|
||||||
|
if @helpBubble?
|
||||||
|
@helpBubble.btOff()
|
||||||
|
@helpBubble = null
|
||||||
|
|
||||||
|
# computes when we should show the master help bubble
|
||||||
|
masterHelpDueTime: () =>
|
||||||
|
dueTime = @showMasterHelpDueTime - new Date().getTime()
|
||||||
|
if dueTime <= 0
|
||||||
|
dueTime = 2000
|
||||||
|
dueTime
|
||||||
|
|
||||||
|
|
||||||
|
# computes when we should show the master help bubble
|
||||||
|
ctaHelpDueTime: () =>
|
||||||
|
dueTime = @showCtaHelpDueTime - new Date().getTime()
|
||||||
|
if dueTime <= 0
|
||||||
|
dueTime = 2000
|
||||||
|
dueTime
|
||||||
|
|
||||||
|
# if the user scrolls, reset the master help due time
|
||||||
|
masterHelpScrollWatch: () =>
|
||||||
|
@clearMasterHelpTimeout()
|
||||||
|
@showMasterHelpTimeout = setTimeout(@showMasterHelp, @masterHelpDueTime() + 2000)
|
||||||
|
|
||||||
|
# if the user scrolls, reset the master help due time
|
||||||
|
ctaHelpScrollWatch: () =>
|
||||||
|
@clearCtaHelpTimeout()
|
||||||
|
@showCtaHelpTimeout = setTimeout(@showCtaHelp, @ctaHelpDueTime() + 2000)
|
||||||
|
|
||||||
|
|
||||||
|
showCtaHelp: () =>
|
||||||
|
@scroller.off('scroll', @ctaHelpScrollWatch)
|
||||||
|
@clearCtaHelpTimeout()
|
||||||
|
|
||||||
|
cutoff = @scroller.offset().top;
|
||||||
|
|
||||||
|
@screen.find('.jamtrack-actions').each((i, element) =>
|
||||||
|
$element = $(element)
|
||||||
|
|
||||||
|
if ($element.offset().top >= cutoff)
|
||||||
|
@helpBubble = context.JK.HelpBubbleHelper.jamtrackBrowseCta($element, $('body'))
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
)
|
||||||
|
|
||||||
|
showMasterHelp: () =>
|
||||||
|
@scroller.off('scroll', @masterHelpScrollWatch)
|
||||||
|
@clearMasterHelpTimeout()
|
||||||
|
|
||||||
|
# don't show the help if the user has already clicked a preview
|
||||||
|
unless @userPreviewed
|
||||||
|
cutoff = @scroller.offset().top;
|
||||||
|
|
||||||
|
@screen.find('.jamtrack-preview[data-track-type="Master"]').each((i, element) =>
|
||||||
|
$element = $(element)
|
||||||
|
|
||||||
|
if ($element.offset().top >= cutoff)
|
||||||
|
@helpBubble = context.JK.HelpBubbleHelper.jamtrackBrowseMasterMix($element.find('.play-button'), $('body'))
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
)
|
||||||
|
|
||||||
|
@showCtaHelpDueTime = new Date().getTime() + 11000
|
||||||
|
@scroller.on('scroll', @ctaHelpScrollWatch)
|
||||||
|
@showCtaHelpTimeout = setTimeout(@showCtaHelp, @ctaHelpDueTime()) # 6000 ms for bubble show time, and 5000ms for delay
|
||||||
|
|
||||||
|
previewPlayed: () =>
|
||||||
|
@userPreviewed = true
|
||||||
|
|
||||||
|
clearCtaHelpTimeout:() =>
|
||||||
|
if @showCtaHelpTimeout?
|
||||||
|
clearTimeout(@showCtaHelpTimeout)
|
||||||
|
@showCtaHelpTimeout = null
|
||||||
|
|
||||||
|
clearBandFilterHelpTimeout: () =>
|
||||||
|
if @showBandFilterHelpTimeout?
|
||||||
|
clearTimeout(@showBandFilterHelpTimeout)
|
||||||
|
@showBandFilterHelpTimeout = null
|
||||||
|
|
||||||
|
clearMasterHelpTimeout: () =>
|
||||||
|
if @showMasterHelpTimeout?
|
||||||
|
clearTimeout(@showMasterHelpTimeout)
|
||||||
|
@showMasterHelpTimeout = null
|
||||||
|
|
||||||
setFilterFromURL:() =>
|
setFilterFromURL:() =>
|
||||||
# Grab parms from URL for artist, instrument, and availability
|
# Grab parms from URL for artist, instrument, and availability
|
||||||
parms=this.getParams()
|
parms=this.getParams()
|
||||||
|
|
@ -73,6 +179,8 @@ context.JK.JamTrackScreen=class JamTrackScreen
|
||||||
@availability.val(parms.availability)
|
@availability.val(parms.availability)
|
||||||
else
|
else
|
||||||
@availability.val('')
|
@availability.val('')
|
||||||
|
|
||||||
|
if window.history.replaceState #ie9 proofing
|
||||||
window.history.replaceState({}, "", "/client#/jamtrackBrowse")
|
window.history.replaceState({}, "", "/client#/jamtrackBrowse")
|
||||||
|
|
||||||
getParams:() =>
|
getParams:() =>
|
||||||
|
|
@ -320,7 +428,8 @@ context.JK.JamTrackScreen=class JamTrackScreen
|
||||||
for track in jamTrack.tracks
|
for track in jamTrack.tracks
|
||||||
trackRow = jamtrackElement.find("[jamtrack-track-id='#{track.id}']")
|
trackRow = jamtrackElement.find("[jamtrack-track-id='#{track.id}']")
|
||||||
previewElement = trackRow.find(".jamtrack-preview")
|
previewElement = trackRow.find(".jamtrack-preview")
|
||||||
new JK.JamTrackPreview(@app, previewElement, jamTrack, track, {master_shows_duration: true, color:'gray'})
|
preview = new JK.JamTrackPreview(@app, previewElement, jamTrack, track, {master_shows_duration: true, color:'gray'})
|
||||||
|
$(preview).on(@EVENTS.PREVIEW_PLAYED, @previewPlayed)
|
||||||
|
|
||||||
this.handleExpanded(jamtrackElement, false)
|
this.handleExpanded(jamtrackElement, false)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ context.JK.JamTrackLanding = class JamTrackLanding
|
||||||
that=this
|
that=this
|
||||||
@bandList.on "click", "a.artist-link", (event)->
|
@bandList.on "click", "a.artist-link", (event)->
|
||||||
context.location="client#/jamtrackBrowse"
|
context.location="client#/jamtrackBrowse"
|
||||||
|
if window.history.replaceState # ie9 proofing
|
||||||
window.history.replaceState({}, "", this.href)
|
window.history.replaceState({}, "", this.href)
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//= require bugsnag
|
||||||
//= require bind-polyfill
|
//= require bind-polyfill
|
||||||
//= require jquery
|
//= require jquery
|
||||||
//= require jquery.monkeypatch
|
//= require jquery.monkeypatch
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,9 @@
|
||||||
submit_data.city = $('#jam_ruby_user_city').val()
|
submit_data.city = $('#jam_ruby_user_city').val()
|
||||||
submit_data.birth_date = gather_birth_date()
|
submit_data.birth_date = gather_birth_date()
|
||||||
submit_data.instruments = gather_instruments()
|
submit_data.instruments = gather_instruments()
|
||||||
|
if($.QueryString['affiliate_partner_id']) {
|
||||||
|
submit_data.affiliate_partner_id = $.QueryString['affiliate_partner_id'];
|
||||||
|
}
|
||||||
//submit_data.photo_url = $('#jam_ruby_user_instruments').val()
|
//submit_data.photo_url = $('#jam_ruby_user_instruments').val()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -630,6 +630,10 @@
|
||||||
return (suppressDay ? '' : (days[date.getDay()] + ' ')) + months[date.getMonth()] + ' ' + context.JK.padString(date.getDate(), 2) + ', ' + date.getFullYear();
|
return (suppressDay ? '' : (days[date.getDay()] + ' ')) + months[date.getMonth()] + ' ' + context.JK.padString(date.getDate(), 2) + ', ' + date.getFullYear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns June for months 0-11
|
||||||
|
context.JK.getMonth = function(monthNumber) {
|
||||||
|
return months[monthNumber];
|
||||||
|
}
|
||||||
|
|
||||||
context.JK.formatDateYYYYMMDD = function(dateString) {
|
context.JK.formatDateYYYYMMDD = function(dateString) {
|
||||||
var date = new Date(dateString);
|
var date = new Date(dateString);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
|
||||||
|
|
||||||
|
$ = jQuery
|
||||||
|
context = window
|
||||||
|
context.JK ||= {};
|
||||||
|
|
||||||
|
class AffiliateLinks
|
||||||
|
constructor: (@app, @partner_id) ->
|
||||||
|
@logger = context.JK.logger
|
||||||
|
@rest = new context.JK.Rest();
|
||||||
|
|
||||||
|
initialize: () =>
|
||||||
|
@page = $('body')
|
||||||
|
@sections = ['jamtrack_songs', 'jamtrack_bands', 'jamtrack_general', 'jamkazam', 'sessions', 'recordings']
|
||||||
|
@jamtrack_songs = @page.find('table.jamtrack_songs tbody')
|
||||||
|
@jamtrack_bands = @page.find('table.jamtrack_bands tbody')
|
||||||
|
@jamtrack_general = @page.find('table.jamtrack_general tbody')
|
||||||
|
@jamkazam = @page.find('table.jamkazam tbody')
|
||||||
|
@sessions= @page.find('table.sessions tbody')
|
||||||
|
@recordings = @page.find('table.recordings tbody')
|
||||||
|
|
||||||
|
@iterate()
|
||||||
|
|
||||||
|
onGetLinks: (links) =>
|
||||||
|
|
||||||
|
$table = @page.find('table.' + @section + ' tbody')
|
||||||
|
template = $('#template-affiliate-link-row').html();
|
||||||
|
context._.each(links, (item) =>
|
||||||
|
$link = $(context._.template(template, item, {variable: 'data'}));
|
||||||
|
$link.find('td.copy-link a').click(@copyLink)
|
||||||
|
$table.append($link)
|
||||||
|
)
|
||||||
|
|
||||||
|
if @sections.length > 0
|
||||||
|
@iterate()
|
||||||
|
|
||||||
|
|
||||||
|
copyLink: () ->
|
||||||
|
$element = $(this)
|
||||||
|
$url = $element.closest('tr').find('td.url input')
|
||||||
|
$url.select()
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
iterate: () =>
|
||||||
|
@section = @sections.shift()
|
||||||
|
|
||||||
|
@rest.getLinks(@section, @partner_id)
|
||||||
|
.done(@onGetLinks)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
context.JK.AffiliateLinks = AffiliateLinks
|
||||||
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
|
||||||
|
|
||||||
|
$ = jQuery
|
||||||
|
context = window
|
||||||
|
context.JK ||= {};
|
||||||
|
|
||||||
|
class AffiliateProgram
|
||||||
|
constructor: (@app) ->
|
||||||
|
@logger = context.JK.logger
|
||||||
|
@rest = new context.JK.Rest();
|
||||||
|
@agreeBtn = null
|
||||||
|
@disagreeBtn = null
|
||||||
|
@entityForm = null
|
||||||
|
@disagreeNotice = null
|
||||||
|
@entityName = null
|
||||||
|
@entityType = null
|
||||||
|
@entityRadio = null
|
||||||
|
@fieldEntityName = null
|
||||||
|
@fieldEntityType = null
|
||||||
|
@entityOptions = null
|
||||||
|
|
||||||
|
removeErrors: () =>
|
||||||
|
@fieldEntityName.removeClass('error').find('.error-info').remove();
|
||||||
|
@fieldEntityType.removeClass('error').find('.error-info').remove();
|
||||||
|
@entityOptions.removeClass('error').find('.error-info').remove();
|
||||||
|
|
||||||
|
onRadioChanged: () =>
|
||||||
|
@removeErrors()
|
||||||
|
value = @page.find('input:radio[name="entity"]:checked').val()
|
||||||
|
|
||||||
|
if value == 'individual'
|
||||||
|
@entityForm.slideUp()
|
||||||
|
else
|
||||||
|
@entityForm.slideDown()
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
onCreatedAffiliatePartner:(response) =>
|
||||||
|
if response.partner_user_id?
|
||||||
|
# this was an existing user, so tell them to go on in
|
||||||
|
context.JK.Banner.show({buttons: [{name: 'GO TO AFFILIATE PAGE', href: '/client#/account/affiliatePartner'}], title: 'congratulations', html: 'Thank you for joining the JamKazam affiliate program!<br/><br/>You can visit the <a href="/client#/account/affiliatePartner">Affiliate Page</a> in your JamKazam Account any time to get links to share to refer users, and to view reports on affiliate activity levels.'})
|
||||||
|
else
|
||||||
|
context.JK.Banner.show({buttons: [{name: 'GO SIGNUP', href:'/signup?affiliate_partner_id=' + response.id}], title: 'congratulations', html: 'Thank you for joining the JamKazam affiliate program!<br/><br/>There is still one more step: you still need to create a user account on JamKazam, so that you can access your affiliate information.'})
|
||||||
|
|
||||||
|
onFailedCreateAffiliatePartner: (jqXHR) =>
|
||||||
|
if jqXHR.status == 422
|
||||||
|
body = JSON.parse(jqXHR.responseText)
|
||||||
|
if body.errors && body.errors.affiliate_partner && body.errors.affiliate_partner[0] == 'You are already an affiliate.'
|
||||||
|
@app.notify({title:'Error', text:'You are already an affiliate.'})
|
||||||
|
else
|
||||||
|
@app.notifyServerError(jqXHR, 'Unable to Create Affiliate')
|
||||||
|
else
|
||||||
|
@app.notifyServerError(jqXHR, 'Unable to Create Affiliate')
|
||||||
|
|
||||||
|
onAgreeClicked: () =>
|
||||||
|
@removeErrors()
|
||||||
|
|
||||||
|
value = @page.find('input:radio[name="entity"]:checked').val()
|
||||||
|
|
||||||
|
error = false
|
||||||
|
|
||||||
|
if value?
|
||||||
|
if value == 'individual'
|
||||||
|
entityType = 'Individual'
|
||||||
|
else
|
||||||
|
# insist that they fill out entity type info
|
||||||
|
entityName = @entityName.val()
|
||||||
|
entityType = @entityType.val()
|
||||||
|
|
||||||
|
entityNameNotEmpty = !!entityName
|
||||||
|
entityTypeNotEmpty = !!entityType
|
||||||
|
|
||||||
|
if !entityNameNotEmpty
|
||||||
|
@fieldEntityName.addClass('error').append('<div class="error-info">must be specified</div>')
|
||||||
|
error = true
|
||||||
|
|
||||||
|
if !entityTypeNotEmpty
|
||||||
|
@fieldEntityType.addClass('error').append('<div class="error-info">must be specified</div>')
|
||||||
|
error = true
|
||||||
|
else
|
||||||
|
@entityOptions.addClass('error')
|
||||||
|
error = true
|
||||||
|
|
||||||
|
unless error
|
||||||
|
@rest.createAffiliatePartner({partner_name: entityName, entity_type: entityType})
|
||||||
|
.done(@onCreatedAffiliatePartner)
|
||||||
|
.fail(@onFailedCreateAffiliatePartner)
|
||||||
|
|
||||||
|
@disagreeNotice.hide ('hidden')
|
||||||
|
return false
|
||||||
|
|
||||||
|
onDisagreeClicked: () =>
|
||||||
|
@removeErrors()
|
||||||
|
|
||||||
|
@disagreeNotice.slideDown('hidden')
|
||||||
|
return false
|
||||||
|
|
||||||
|
events:() =>
|
||||||
|
@entityRadio.on('change', @onRadioChanged)
|
||||||
|
@agreeBtn.on('click', @onAgreeClicked)
|
||||||
|
@disagreeBtn.on('click', @onDisagreeClicked)
|
||||||
|
|
||||||
|
initialize: () =>
|
||||||
|
@page = $('body')
|
||||||
|
@agreeBtn = @page.find('.agree-button')
|
||||||
|
@disagreeBtn = @page.find('.disagree-button')
|
||||||
|
@entityForm = @page.find('.entity-info')
|
||||||
|
@disagreeNotice = @page.find('.disagree-text')
|
||||||
|
@entityName = @page.find('input[name="entity-name"]')
|
||||||
|
@entityType = @page.find('select[name="entity-type"]')
|
||||||
|
@entityRadio = @page.find('input[name="entity"]')
|
||||||
|
@fieldEntityName = @page.find('.field.entity.name')
|
||||||
|
@fieldEntityType = @page.find('.field.entity.type')
|
||||||
|
@entityOptions = @page.find('.entity-options')
|
||||||
|
|
||||||
|
@events()
|
||||||
|
|
||||||
|
context.JK.AffiliateProgram = AffiliateProgram
|
||||||
|
|
@ -44,12 +44,14 @@
|
||||||
|
|
||||||
$previews.append($element);
|
$previews.append($element);
|
||||||
|
|
||||||
new context.JK.JamTrackPreview(app, $element, jam_track, track, {master_shows_duration: false, color:'black', master_adds_line_break: true})
|
new context.JK.JamTrackPreview(app, $element, jam_track, track, {master_shows_duration: false, color:'black', master_adds_line_break: true, preload_master:true})
|
||||||
|
|
||||||
|
if(track.track_type =='Master') {
|
||||||
|
context.JK.HelpBubbleHelper.rotateJamTrackLandingBubbles($element.find('.jam-track-preview'), $page.find('.video-wrapper'), $page.find('.cta-free-jamtrack a'), $page.find('a.browse-jamtracks'));
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
$previews.append('<br clear = "all" />')
|
$previews.append('<br clear = "all" />')
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.fail(function () {
|
.fail(function () {
|
||||||
app.notify({title: 'Unable to fetch JamTrack', text: "Please refresh the page or try again later."})
|
app.notify({title: 'Unable to fetch JamTrack', text: "Please refresh the page or try again later."})
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,11 @@
|
||||||
|
|
||||||
$previews.append($element);
|
$previews.append($element);
|
||||||
|
|
||||||
new context.JK.JamTrackPreview(app, $element, jam_track, track, {master_shows_duration: false, color:'black', master_adds_line_break:true})
|
new context.JK.JamTrackPreview(app, $element, jam_track, track, {master_shows_duration: false, color:'black', master_adds_line_break:true, preload_master:true})
|
||||||
|
|
||||||
|
if(track.track_type =='Master') {
|
||||||
|
context.JK.HelpBubbleHelper.rotateJamTrackLandingBubbles($element.find('.jam-track-preview'), $page.find('.video-wrapper'), $page.find('.cta-free-jamtrack a'), $page.find('a.browse-jamtracks'));
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
$previews.append('<br clear = "all" />')
|
$previews.append('<br clear = "all" />')
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//= require bugsnag
|
||||||
//= require bind-polyfill
|
//= require bind-polyfill
|
||||||
//= require jquery
|
//= require jquery
|
||||||
//= require jquery.monkeypatch
|
//= require jquery.monkeypatch
|
||||||
|
|
@ -20,6 +21,7 @@
|
||||||
//= require jquery.icheck
|
//= require jquery.icheck
|
||||||
//= require jquery.bt
|
//= require jquery.bt
|
||||||
//= require jquery.exists
|
//= require jquery.exists
|
||||||
|
//= require jquery.visible
|
||||||
//= require howler.core.js
|
//= require howler.core.js
|
||||||
//= require AAA_Log
|
//= require AAA_Log
|
||||||
//= require AAC_underscore
|
//= require AAC_underscore
|
||||||
|
|
@ -65,6 +67,8 @@
|
||||||
//= require web/tracking
|
//= require web/tracking
|
||||||
//= require web/individual_jamtrack
|
//= require web/individual_jamtrack
|
||||||
//= require web/individual_jamtrack_band
|
//= require web/individual_jamtrack_band
|
||||||
|
//= require web/affiliate_program
|
||||||
|
//= require web/affiliate_links
|
||||||
//= require fakeJamClient
|
//= require fakeJamClient
|
||||||
//= require fakeJamClientMessages
|
//= require fakeJamClientMessages
|
||||||
//= require fakeJamClientRecordings
|
//= require fakeJamClientRecordings
|
||||||
|
|
|
||||||
|
|
@ -884,7 +884,11 @@
|
||||||
// deal with sample rate
|
// deal with sample rate
|
||||||
if(selectedDeviceInfo && context.jamClient.FTUESetPreferredMixerSampleRate) {
|
if(selectedDeviceInfo && context.jamClient.FTUESetPreferredMixerSampleRate) {
|
||||||
// get the preferred sample rate for the device, and set that as the initial value of the sample rate dropdown
|
// get the preferred sample rate for the device, and set that as the initial value of the sample rate dropdown
|
||||||
if (selectedDeviceInfo.input.info.port_audio_name != 'Default Input') {
|
// except for WKS; we just default to 48
|
||||||
|
if (selectedDeviceInfo.input.info.type == 'Win32_wdm') {
|
||||||
|
var inputSampleRate = 48000;
|
||||||
|
}
|
||||||
|
else if (selectedDeviceInfo.input.info.port_audio_name != 'Default Input') {
|
||||||
var inputSampleRate = context.jamClient.FTUEgetInputDeviceSampleRate(selectedDeviceInfo.input.info.port_audio_name);
|
var inputSampleRate = context.jamClient.FTUEgetInputDeviceSampleRate(selectedDeviceInfo.input.info.port_audio_name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var devices = context.jamClient.FTUEGetAudioDevices();
|
var devices = context.jamClient.FTUEGetAudioDevices();
|
||||||
logger.debug("FTUEGetAudioDevices: " + JSON.stringify(devices));
|
//logger.debug("FTUEGetAudioDevices: " + JSON.stringify(devices));
|
||||||
|
|
||||||
var loadedDevices = {};
|
var loadedDevices = {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
height:0;
|
||||||
|
border-width: 1px 0 0 0;
|
||||||
|
border-style:solid;
|
||||||
|
border-color:$ColorTextTypical;
|
||||||
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
margin-top:8px;
|
margin-top:8px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
@import 'common.css.scss';
|
||||||
|
|
||||||
|
#account-affiliate-partner {
|
||||||
|
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 125%;
|
||||||
|
margin:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.affiliates-header {
|
||||||
|
float:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.affiliate-partner-nav {
|
||||||
|
width:85%;
|
||||||
|
position:relative;
|
||||||
|
float:right;
|
||||||
|
margin-bottom:20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.affiliate-partner-nav a {
|
||||||
|
width:19%;
|
||||||
|
text-align:center;
|
||||||
|
height: 27px;
|
||||||
|
display: block;
|
||||||
|
float:right;
|
||||||
|
margin-right:5px;
|
||||||
|
vertical-align:bottom;
|
||||||
|
padding-top:10px;
|
||||||
|
background-color:#535353;
|
||||||
|
color:#ccc;
|
||||||
|
font-size:17px;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.affiliate-partner-nav a:hover {
|
||||||
|
background-color:#666;
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.affiliate-partner-nav a.active {
|
||||||
|
background-color:#ed3618;
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.affiliate-partner-nav a.active:hover {
|
||||||
|
background-color:#ed3618;
|
||||||
|
cursor:default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.affiliate-partner-nav a.last {
|
||||||
|
margin-right:0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#affiliate-partner-tab-content {
|
||||||
|
.tab-account {
|
||||||
|
.left-col {
|
||||||
|
float: left;
|
||||||
|
width: 55%;
|
||||||
|
.affiliate-label {
|
||||||
|
text-align: left;
|
||||||
|
width: 130px;
|
||||||
|
float: left;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
@include border_box_sizing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-orange {
|
||||||
|
margin-right:2px;
|
||||||
|
}
|
||||||
|
.spacer {
|
||||||
|
width:140px;
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
.input-buttons {
|
||||||
|
width:60%;
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right-col {
|
||||||
|
float: right;
|
||||||
|
width: 45%;
|
||||||
|
margin-top: 30px;
|
||||||
|
line-height:125%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.links {
|
||||||
|
p.prompt {
|
||||||
|
margin-bottom:20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-type-section {
|
||||||
|
border-width:0 0 1px 0;
|
||||||
|
border-style:solid;
|
||||||
|
border-color:$ColorTextTypical;
|
||||||
|
padding-bottom:20px;
|
||||||
|
margin-bottom:20px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
display:inline-block;
|
||||||
|
margin-right:20px;
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-type-prompt {
|
||||||
|
display:none;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom:20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-link {
|
||||||
|
font-weight:bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.traffic-table {
|
||||||
|
width:400px;
|
||||||
|
.signups, .visits {
|
||||||
|
width:70px;
|
||||||
|
text-align:right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.links-table {
|
||||||
|
min-width:100%;
|
||||||
|
margin-top:20px;
|
||||||
|
|
||||||
|
th {
|
||||||
|
padding: 10px 0 20px;
|
||||||
|
white-space:nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding:3px 0;
|
||||||
|
white-space:nowrap;
|
||||||
|
}
|
||||||
|
.target {
|
||||||
|
width:45%;
|
||||||
|
white-space:normal;
|
||||||
|
}
|
||||||
|
.copy-link {
|
||||||
|
width:100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url {
|
||||||
|
|
||||||
|
input {
|
||||||
|
background-color: transparent;
|
||||||
|
-webkit-box-shadow:none;
|
||||||
|
box-shadow:none;
|
||||||
|
color:#ccc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.agreement {
|
||||||
|
#partner-agreement-v1 {
|
||||||
|
height:340px;
|
||||||
|
overflow:scroll;
|
||||||
|
padding:20px;
|
||||||
|
border:1px solid white;
|
||||||
|
@include border-box_sizing;
|
||||||
|
}
|
||||||
|
|
||||||
|
label.partner-agreement {
|
||||||
|
display:inline-block;
|
||||||
|
margin-right:40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size:20px;
|
||||||
|
font-weight:bold;
|
||||||
|
margin:40px 30px 20px 0;
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.execution-date {
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-buttons {
|
||||||
|
margin-top:20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
* compiled file, but it's generally better to create a new file per style scope.
|
* compiled file, but it's generally better to create a new file per style scope.
|
||||||
*
|
*
|
||||||
*= require_self
|
*= require_self
|
||||||
|
*= require web/Raleway
|
||||||
*= require jquery.ui.datepicker
|
*= require jquery.ui.datepicker
|
||||||
*= require ./ie
|
*= require ./ie
|
||||||
*= require jquery.bt
|
*= require jquery.bt
|
||||||
|
|
@ -35,6 +36,7 @@
|
||||||
*= require ./accountProfileInterests
|
*= require ./accountProfileInterests
|
||||||
*= require ./accountProfileSamples
|
*= require ./accountProfileSamples
|
||||||
*= require ./accountPaymentHistory
|
*= require ./accountPaymentHistory
|
||||||
|
*= require ./account_affiliate
|
||||||
*= require ./search
|
*= require ./search
|
||||||
*= require ./ftue
|
*= require ./ftue
|
||||||
*= require ./jamServer
|
*= require ./jamServer
|
||||||
|
|
@ -81,4 +83,5 @@
|
||||||
*= require ./downloadJamTrack
|
*= require ./downloadJamTrack
|
||||||
*= require ./jamTrackPreview
|
*= require ./jamTrackPreview
|
||||||
*= require users/signinCommon
|
*= require users/signinCommon
|
||||||
|
*= require landings/partner_agreement_v1
|
||||||
*/
|
*/
|
||||||
|
|
@ -50,6 +50,8 @@ body.jam, body.web, .dialog{
|
||||||
|
|
||||||
.big-help {
|
.big-help {
|
||||||
font-size:20px;
|
font-size:20px;
|
||||||
|
color: #ed3618;
|
||||||
|
p {color:#ed3618}
|
||||||
}
|
}
|
||||||
|
|
||||||
.help-hover-recorded-tracks, .help-hover-stream-mix, .help-hover-recorded-backing-tracks {
|
.help-hover-recorded-tracks, .help-hover-stream-mix, .help-hover-recorded-backing-tracks {
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,19 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
position:absolute;
|
||||||
|
right:-135px;
|
||||||
|
top:0;
|
||||||
|
padding:0 3px;
|
||||||
|
font-style:italic;
|
||||||
|
z-index:1;
|
||||||
|
-webkit-border-radius:10px;
|
||||||
|
-moz-border-radius:10px;
|
||||||
|
border-radius:10px;
|
||||||
|
border:0 solid black;
|
||||||
|
}
|
||||||
|
|
||||||
.adds-line-break {
|
.adds-line-break {
|
||||||
display:block;
|
display:block;
|
||||||
margin-left:66px;
|
margin-left:66px;
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,11 @@ body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family: Raleway, Arial, Helvetica, sans-serif;
|
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
|
|
||||||
input,textarea {
|
input,textarea {
|
||||||
font-family: Raleway, Arial, Helvetica, sans-serif;
|
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -556,22 +556,22 @@ hr {
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-input-placeholder {
|
::-webkit-input-placeholder {
|
||||||
font-family: Raleway, Arial, Helvetica, sans-serif;
|
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||||
font-size:14px;
|
font-size:14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:-moz-placeholder { /* Firefox 18- */
|
:-moz-placeholder { /* Firefox 18- */
|
||||||
font-family: Raleway, Arial, Helvetica, sans-serif;
|
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||||
font-size:14px;
|
font-size:14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-moz-placeholder { /* Firefox 19+ */
|
::-moz-placeholder { /* Firefox 19+ */
|
||||||
font-family: Raleway, Arial, Helvetica, sans-serif;
|
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||||
font-size:14px;
|
font-size:14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:-ms-input-placeholder {
|
:-ms-input-placeholder {
|
||||||
font-family: Raleway, Arial, Helvetica, sans-serif;
|
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||||
font-size:14px;
|
font-size:14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -198,20 +198,21 @@
|
||||||
.jam-track-preview {
|
.jam-track-preview {
|
||||||
font-size:11px;
|
font-size:11px;
|
||||||
white-space:nowrap;
|
white-space:nowrap;
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
right:-115px;
|
||||||
|
background-color:#262626;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.jamtrack-action {
|
.jamtrack-action {
|
||||||
@include border_box_sizing;
|
@include border_box_sizing;
|
||||||
width: 20%;
|
width: 20%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
.jamtrack-action-container {
|
.jamtrack-action-container {
|
||||||
display: flex;
|
display:inline-block;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
position:absolute;
|
|
||||||
height:100%;
|
|
||||||
width:100%;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.play-button {
|
.play-button {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
overflow:auto;
|
overflow:auto;
|
||||||
border:1px solid #ED3618;
|
border:1px solid #ED3618;
|
||||||
text-align:left;
|
text-align:left;
|
||||||
font-family: Raleway, Arial, Helvetica, sans-serif;
|
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||||
ul {
|
ul {
|
||||||
height:100%;
|
height:100%;
|
||||||
margin-left:20px;
|
margin-left:20px;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
overflow:auto;
|
overflow:auto;
|
||||||
border:1px solid #ED3618;
|
border:1px solid #ED3618;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
font-family: Raleway, Arial, Helvetica, sans-serif;
|
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||||
ul {
|
ul {
|
||||||
@include vertical-align-column;
|
@include vertical-align-column;
|
||||||
height:100%;
|
height:100%;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
@import "client/common";
|
@import "client/common";
|
||||||
|
|
||||||
|
|
||||||
table.findsession-table, table.local-recordings, table.open-jam-tracks, table.open-backing-tracks, table.cart-items, #account-session-detail, table.payment-table {
|
table.findsession-table, table.local-recordings, table.open-jam-tracks, table.open-backing-tracks, table.cart-items, #account-session-detail, table.payment-table, table.jamtable {
|
||||||
|
|
||||||
.latency-unacceptable {
|
.latency-unacceptable {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
|
|
@ -64,7 +64,7 @@ table.findsession-table, table.local-recordings, table.open-jam-tracks, table.op
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
table.findsession-table, table.local-recordings, table.open-jam-tracks, table.open-backing-tracks, table.cart-items, table.payment-table {
|
table.findsession-table, table.local-recordings, table.open-jam-tracks, table.open-backing-tracks, table.cart-items, table.payment-table, table.jamtable {
|
||||||
width:98%;
|
width:98%;
|
||||||
height:10%;
|
height:10%;
|
||||||
font-size:11px;
|
font-size:11px;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
/**
|
/**
|
||||||
|
*= require web/Raleway
|
||||||
*= require client/ie
|
*= require client/ie
|
||||||
*= require client/jamkazam
|
*= require client/jamkazam
|
||||||
*= require client/header
|
*= require client/header
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
/**
|
/**
|
||||||
|
*= require web/Raleway
|
||||||
*= require client/ie
|
*= require client/ie
|
||||||
*= require client/jamkazam
|
*= require client/jamkazam
|
||||||
*= require client/screen_common
|
*= require client/screen_common
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
@import "client/common.css.scss";
|
||||||
|
|
||||||
|
body.web.landing_page.full {
|
||||||
|
&.affiliate_program {
|
||||||
|
|
||||||
|
.video-container {
|
||||||
|
width:100%;
|
||||||
|
padding-bottom:53.33%;
|
||||||
|
|
||||||
|
}
|
||||||
|
.agree-disagree-buttons {
|
||||||
|
margin-top:30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agree-button {
|
||||||
|
margin-right:20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partner-agreement-v1 {
|
||||||
|
height:340px;
|
||||||
|
overflow:scroll;
|
||||||
|
padding:20px;
|
||||||
|
border:1px solid white;
|
||||||
|
@include border-box_sizing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
padding-top:20px;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom:20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
@include border_box_sizing;
|
||||||
|
width:50% ! important;
|
||||||
|
float:left;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom:15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display:inline-block;
|
||||||
|
color:#ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.agreement-notice {
|
||||||
|
color:white;
|
||||||
|
margin:20px 0 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field.radio {
|
||||||
|
margin-top:10px;
|
||||||
|
label {
|
||||||
|
margin-left:10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.field.entity {
|
||||||
|
&.name {
|
||||||
|
margin-top:30px;
|
||||||
|
}
|
||||||
|
margin-top:10px;
|
||||||
|
width:400px;
|
||||||
|
input, select {
|
||||||
|
width:300px;
|
||||||
|
@include border_box_sizing;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
width:100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +1,5 @@
|
||||||
/**
|
/**
|
||||||
|
*= require web/Raleway
|
||||||
*= require client/ie
|
*= require client/ie
|
||||||
*= require client/jamkazam
|
*= require client/jamkazam
|
||||||
*= require client/screen_common
|
*= require client/screen_common
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Raleway';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100;
|
||||||
|
src: local('Raleway Thin'), local('Raleway-Thin'), url(/assets/Raleway/Raleway-Thin.ttf) format('truetype'), url(/assets/Raleway/Raleway-Thin.woff2) format('woff2'), url(/assets/Raleway/Raleway-Thin.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Raleway';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
src: local('Raleway Light'), local('Raleway-Light'), url(/assets/Raleway/Raleway-Light.ttf) format('truetype'), url(/assets/Raleway/Raleway-Light.woff2) format('woff2'), url(/assets/Raleway/Raleway-Light.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Raleway';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url(/assets/Raleway/Raleway.eot);
|
||||||
|
src: local('Raleway'), url(/assets/Raleway/Raleway.eot?#iefix) format('embedded-opentype'), url(/assets/Raleway/Raleway.svg#Raleway) format('svg'), url(/assets/Raleway/Raleway.ttf) format('truetype'), url(/assets/Raleway/Raleway.woff2) format('woff2'), url(/assets/Raleway/Raleway.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Raleway';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Raleway Bold'), local('Raleway-Bold'), url(/assets/Raleway/Raleway-Bold.ttf) format('truetype'), url(/assets/Raleway/Raleway-Bold.woff2) format('woff2'), url(/assets/Raleway/Raleway-Bold.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Raleway';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 900;
|
||||||
|
src: local('Raleway Heavy'), local('Raleway-Heavy'), url(/assets/Raleway/Raleway-Heavy.ttf) format('truetype'), url(/assets/Raleway/Raleway-Heavy.woff2) format('woff2'), url(/assets/Raleway/Raleway-Heavy.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue