2014-04-20 22:54:49 +00:00
class JamRuby :: AffiliatePartner < ActiveRecord :: Base
2015-05-28 13:20:14 +00:00
self . table_name = 'affiliate_partners'
2014-04-20 22:54:49 +00:00
2015-05-28 13:20:14 +00:00
belongs_to :partner_user , :class_name = > " JamRuby::User " , :foreign_key = > :partner_user_id , inverse_of : :affiliate_partner
2016-05-16 16:39:20 +00:00
has_one :school , class_name : " JamRuby::School "
2016-08-31 09:19:16 +00:00
has_one :retailer , class_name : " JamRuby::Retailer "
2015-05-28 13:20:14 +00:00
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
2016-04-06 02:23:15 +00:00
has_many :affiliate_distributions , :class_name = > " JamRuby::AffiliateDistribution " , foreign_key : :affiliate_referral_id
2021-02-15 04:32:27 +00:00
has_many :links , :class_name = > " JamRuby::AffiliateLink " , foreign_key : :affiliate_partner_id
2015-10-21 02:09:58 +00:00
attr_accessible :partner_name , :partner_code , :partner_user_id , :entity_type , :rate , as : :admin
2014-04-20 22:54:49 +00:00
2015-05-28 13:20:14 +00:00
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'
2017-03-22 12:39:06 +00:00
GUITAR_CENTER = 'guitar_center'
2015-05-28 13:20:14 +00:00
# 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 = > '' ,
}
2014-04-22 01:55:40 +00:00
PARAM_REFERRAL = :ref
PARAM_COOKIE = :affiliate_ref
2014-04-20 23:20:27 +00:00
PARTNER_CODE_REGEX = / ^[ #{ Regexp . escape ( 'abcdefghijklmnopqrstuvwxyz0123456789-._+,' ) } ]+{2,128}$ /i
2015-05-28 13:20:14 +00:00
#validates :user_email, format: {with: JamRuby::User::VALID_EMAIL_REGEX}, :if => :user_email
#validates :partner_code, format: { with: PARTNER_CODE_REGEX }, :allow_blank => true
validates :entity_type , inclusion : { in : ENTITY_TYPES , message : " invalid entity type " }
2016-07-17 15:16:27 +00:00
#serialize :address, JSON
2014-04-20 22:54:49 +00:00
2015-05-28 13:20:14 +00:00
before_save do | record |
record . address || = ADDRESS_SCHEMA . clone
record . entity_type || = ENTITY_TYPES . first
2021-02-15 04:32:27 +00:00
record . partner_user_id = nil if record . partner_user_id == '' #for activeadmin coercion
2015-05-28 13:20:14 +00:00
end
2015-10-18 14:05:24 +00:00
def display_name
partner_name || ( partner_user ? partner_user . name : 'abandoned' )
end
2015-10-19 19:34:06 +00:00
def admin_url
2015-10-19 22:53:50 +00:00
APP_CONFIG . admin_root_url + " /admin/affiliates/ #{ id } "
2015-10-19 19:34:06 +00:00
end
2015-05-28 13:20:14 +00:00
# used by admin
2014-04-20 22:54:49 +00:00
def self . create_with_params ( params = { } )
2015-05-28 13:20:14 +00:00
raise 'not supported'
2016-05-16 16:39:20 +00:00
oo = AffiliatePartner . new
2014-04-20 23:20:27 +00:00
oo . partner_name = params [ :partner_name ] . try ( :strip )
2014-04-22 02:14:22 +00:00
oo . partner_code = params [ :partner_code ] . try ( :strip ) . try ( :downcase )
2014-04-20 23:20:27 +00:00
oo . partner_user = User . where ( :email = > params [ :user_email ] . try ( :strip ) ) . limit ( 1 ) . first
2014-04-20 22:54:49 +00:00
oo . partner_user_id = oo . partner_user . try ( :id )
2015-05-28 13:20:14 +00:00
oo . entity_type = params [ :entity_type ] || ENTITY_TYPES . first
oo . save
oo
end
# used by web
def self . create_with_web_params ( user , params = { } )
2016-05-16 16:39:20 +00:00
oo = AffiliatePartner . new
2015-05-28 13:20:14 +00:00
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
2016-08-03 01:46:15 +00:00
oo . signed_at = Time . now
2014-04-22 01:55:40 +00:00
oo . save
2014-04-20 22:54:49 +00:00
oo
end
2016-05-16 16:39:20 +00:00
def self . create_from_school ( school )
oo = AffiliatePartner . new
oo . partner_name = " Affiliate from School #{ school . id } "
oo . partner_user = school . owner
oo . entity_type = 'Other'
oo . school = school
oo . signed_at = nil
oo . save
end
2016-08-31 09:19:16 +00:00
def self . create_from_retailer ( retailer )
oo = AffiliatePartner . new
oo . partner_name = " Affiliate from Retailer #{ retailer . id } "
oo . partner_user = retailer . owner
oo . entity_type = 'Other'
oo . retailer = retailer
oo . signed_at = nil
oo . save
end
2014-04-20 22:54:49 +00:00
def self . coded_id ( code = nil )
self . where ( :partner_code = > code ) . limit ( 1 ) . pluck ( :id ) . first if code . present?
end
2014-04-22 02:14:22 +00:00
def self . is_code? ( code )
2015-05-28 13:20:14 +00:00
self . where ( :partner_code = > code ) . limit ( 1 ) . pluck ( :id ) . present?
2014-04-22 02:14:22 +00:00
end
2014-04-20 22:54:49 +00:00
2014-04-22 07:12:05 +00:00
def referrals_by_date
2014-04-23 06:34:21 +00:00
by_date = User . where ( :affiliate_referral_id = > self . id )
2015-05-28 13:20:14 +00:00
. group ( 'DATE(created_at)' )
. having ( " COUNT(*) > 0 " )
. order ( 'date_created_at DESC' )
. count
2014-04-23 06:34:21 +00:00
block_given? ? yield ( by_date ) : by_date
2014-04-22 07:12:05 +00:00
end
2015-05-28 13:20:14 +00:00
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 )
2021-02-15 04:32:27 +00:00
sale_time - user . created_at < 3 . years
2015-05-28 13:20:14 +00:00
end
2016-04-06 02:23:15 +00:00
def should_attribute_sale? ( shopping_cart , user_to_check , instance )
2021-02-15 04:32:27 +00:00
raise " Not a JamTrack sale " if ! shopping_cart . is_jam_track?
2016-04-06 02:23:15 +00:00
if created_within_affiliate_window ( user_to_check , Time . now )
product_info = shopping_cart . product_info ( instance )
2015-11-29 19:58:10 +00:00
# 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
2016-04-06 02:23:15 +00:00
2025-03-11 04:24:59 +00:00
{ fee_in_cents : ( product_info [ :real_price ] * 100 * rate ) . round }
2021-02-15 04:32:27 +00:00
# if shopping_cart.is_lesson?
# applicable_rate = lesson_rate
# else
# applicable_rate = rate
# end
#{fee_in_cents: (product_info[:price] * 100 * real_quantity * applicable_rate.to_f).round}
2025-03-07 22:38:44 +00:00
# { fee_in_cents: (real_quantity * jamtrack_share_in_cents.to_f).round}
2015-05-28 13:20:14 +00:00
else
2015-11-29 19:58:10 +00:00
false
2015-05-28 13:20:14 +00:00
end
end
def cumulative_earnings_in_dollars
cumulative_earnings_in_cents . to_f / 100 . to_f
end
2025-03-23 18:54:53 +00:00
def jamtrack_cumulative_earnings_in_dollars
jamtrack_cumulative_earnings_in_cents . to_f / 100 . to_f
end
def subscriptions_cumulative_earnings_in_dollars
subscriptions_cumulative_earnings_in_cents . to_f / 100 . to_f
end
def current_quarter_in_dollars
current_quarter_in_cents . to_f / 100 . to_f
end
def jamtrack_current_quarter_in_dollars
jamtrack_cumulative_earnings_in_cents . to_f / 100 . to_f
end
def subscriptions_current_quarter_in_dollars
subscriptions_current_quarter_in_cents . to_f / 100 . to_f
end
2015-05-28 13:20:14 +00:00
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 ] )
2025-03-07 22:38:44 +00:00
# we aren't tracking visits anymore, so we don't need to tally_traffic_totals
#tally_traffic_totals(GenericState.affiliate_tallied_at, day)
2015-05-28 13:20:14 +00:00
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 )
%{
2016-04-06 02:23:15 +00:00
FROM affiliate_distributions inner join sale_line_items ON affiliate_distributions . sale_line_item_id = sale_line_items . id
2015-05-28 13:20:14 +00:00
WHERE
2016-04-06 02:23:15 +00:00
( DATE ( affiliate_distributions . created_at ) > = DATE ( '#{start_date}' ) AND DATE ( affiliate_distributions . created_at ) < = DATE ( '#{end_date}' ) )
2015-05-28 13:20:14 +00:00
AND
2016-04-06 02:23:15 +00:00
affiliate_distributions . affiliate_referral_id = #{table_name}.affiliate_partner_id
2015-05-28 13:20:14 +00:00
}
end
def self . sale_items_refunded_subquery ( start_date , end_date , table_name )
%{
2016-04-06 02:23:15 +00:00
FROM affiliate_distributions inner join sale_line_items ON affiliate_distributions . sale_line_item_id = sale_line_items . id
2015-05-28 13:20:14 +00:00
WHERE
2016-04-06 02:23:15 +00:00
( DATE ( affiliate_distributions . affiliate_refunded_at ) > = DATE ( '#{start_date}' ) AND DATE ( affiliate_distributions . affiliate_refunded_at ) < = DATE ( '#{end_date}' ) )
2015-05-28 13:20:14 +00:00
AND
2016-04-06 02:23:15 +00:00
affiliate_distributions . affiliate_referral_id = #{table_name}.affiliate_partner_id
2015-05-28 13:20:14 +00:00
AND
2016-04-06 02:23:15 +00:00
affiliate_distributions . affiliate_refunded = TRUE
2015-05-28 13:20:14 +00:00
}
end
2021-02-15 04:32:27 +00:00
def self . subscription_distribution_sub_query ( start_date , end_date , table_name )
%{
FROM affiliate_distributions INNER JOIN affiliate_partners
ON affiliate_distributions . affiliate_referral_id = affiliate_partners . id
WHERE
affiliate_distributions . product_type = 'Subscription'
AND
( DATE ( affiliate_distributions . created_at ) > = DATE ( '#{start_date}' ) )
AND
( DATE ( affiliate_distributions . created_at ) < = DATE ( '#{end_date}' ) )
AND affiliate_distributions . affiliate_referral_id = #{table_name}.affiliate_partner_id
}
end
2015-05-28 13:20:14 +00:00
# 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 (
2016-04-06 02:23:15 +00:00
( SELECT COUNT ( CASE WHEN sale_line_items . product_type = 'JamTrack' AND affiliate_distributions . affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END )
2015-05-28 13:20:14 +00:00
#{sale_items_subquery(start_date, end_date, 'affiliate_monthly_payments')}
) , 0 )
+
COALESCE (
2016-04-06 02:23:15 +00:00
( SELECT - COUNT ( CASE WHEN sale_line_items . product_type = 'JamTrack' AND affiliate_distributions . affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END )
2015-05-28 13:20:14 +00:00
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_monthly_payments')}
) , 0 ) ,
2025-03-23 18:54:53 +00:00
subscriptions_count =
COALESCE (
( SELECT COUNT ( CASE WHEN affiliate_distributions . affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END )
#{subscription_distribution_sub_query(start_date, end_date, 'affiliate_monthly_payments')}
) , 0 ) ,
2015-05-28 13:20:14 +00:00
due_amount_in_cents =
COALESCE (
2016-04-06 02:23:15 +00:00
( SELECT SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
2015-05-28 13:20:14 +00:00
#{sale_items_subquery(start_date, end_date, 'affiliate_monthly_payments')}
) , 0 )
+
COALESCE (
2016-04-06 02:23:15 +00:00
( SELECT - SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
2015-05-28 13:20:14 +00:00
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_monthly_payments')}
) , 0 )
2021-02-15 04:32:27 +00:00
+
COALESCE (
( SELECT SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
#{subscription_distribution_sub_query(start_date, end_date, 'affiliate_monthly_payments')}
2025-03-23 18:54:53 +00:00
) , 0 ) ,
jamtrack_due_amount_in_cents =
COALESCE (
( SELECT SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
#{sale_items_subquery(start_date, end_date, 'affiliate_monthly_payments')}
) , 0 )
+
COALESCE (
( SELECT - SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_monthly_payments')}
) , 0 ) ,
subscription_due_amount_in_cents =
COALESCE (
( SELECT SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
#{subscription_distribution_sub_query(start_date, end_date, 'affiliate_monthly_payments')}
2021-02-15 04:32:27 +00:00
) , 0 )
2015-05-28 13:20:14 +00:00
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 ( )
2021-03-14 21:23:43 +00:00
WHERE year < #{year} AND month < #{month}
2015-05-28 13:20:14 +00:00
}
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 (
2016-04-06 02:23:15 +00:00
( SELECT COUNT ( CASE WHEN sale_line_items . product_type = 'JamTrack' AND affiliate_distributions . affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END )
2015-05-28 13:20:14 +00:00
#{sale_items_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
) , 0 )
+
COALESCE (
2016-04-06 02:23:15 +00:00
( SELECT - COUNT ( CASE WHEN sale_line_items . product_type = 'JamTrack' AND affiliate_distributions . affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END )
2015-05-28 13:20:14 +00:00
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
) , 0 ) ,
2025-03-23 18:54:53 +00:00
subscriptions_count =
COALESCE (
( SELECT COUNT ( CASE WHEN affiliate_distributions . affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END )
#{subscription_distribution_sub_query(start_date, end_date, 'affiliate_quarterly_payments')}
) , 0 ) ,
2015-05-28 13:20:14 +00:00
due_amount_in_cents =
COALESCE (
2016-04-06 02:23:15 +00:00
( SELECT SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
2015-05-28 13:20:14 +00:00
#{sale_items_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
) , 0 )
+
COALESCE (
2016-04-06 02:23:15 +00:00
( SELECT - SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
2015-05-28 13:20:14 +00:00
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
) , 0 )
2021-02-15 04:32:27 +00:00
+
COALESCE (
( SELECT SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
#{subscription_distribution_sub_query(start_date, end_date, 'affiliate_quarterly_payments')}
2025-03-23 18:54:53 +00:00
) , 0 ) ,
jamtrack_due_amount_in_cents =
COALESCE (
( SELECT SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
#{sale_items_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
) , 0 )
+
COALESCE (
( SELECT - SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
) , 0 ) ,
subscription_due_amount_in_cents =
COALESCE (
( SELECT SUM ( affiliate_distributions . affiliate_referral_fee_in_cents )
#{subscription_distribution_sub_query(start_date, end_date, 'affiliate_quarterly_payments')}
2021-02-15 04:32:27 +00:00
) , 0 )
2015-05-28 13:20:14 +00:00
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 ( )
2016-04-21 14:23:29 +00:00
WHERE year < #{year} OR (year = #{year} AND quarter < #{quarter})
2015-05-28 13:20:14 +00:00
}
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
2025-03-07 22:38:44 +00:00
# This was added because the tally_traffic_totals runs once a day, which is not often enough to get a fresh count of signups
# so as users sign up, we increment the signups count for the day
# jam=# \d affiliate_traffic_totals
# Table "public.affiliate_traffic_totals"
# Column | Type | Modifiers
#----------------------+-----------------------------+------------------------
# day | date | not null
# signups | integer | not null default 0
# visits | integer | not null default 0
# affiliate_partner_id | integer | not null
# created_at | timestamp without time zone | not null default now()
def self . increment_signups ( user )
sql = " SELECT count(day) as count FROM affiliate_traffic_totals WHERE day = ' #{ user . created_at . to_date } ' AND affiliate_partner_id = #{ user . affiliate_referral_id } "
count = ActiveRecord :: Base . connection . execute ( sql )
if count . count > 0 && count [ 0 ] [ 'count' ] . to_i && count [ 0 ] [ 'count' ] . to_i > 0
sql = %{
UPDATE affiliate_traffic_totals SET signups = signups + 1 WHERE day = '#{user.created_at.to_date}' AND affiliate_partner_id = #{user.affiliate_referral_id}
}
else
sql = %{
INSERT INTO affiliate_traffic_totals ( day , signups , visits , affiliate_partner_id ) VALUES ( '#{user.created_at.to_date}' , 1 , 0 , #{user.affiliate_referral_id})
}
end
ActiveRecord :: Base . connection . execute ( sql )
end
2015-05-28 13:20:14 +00:00
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 ) ,
2025-03-23 18:54:53 +00:00
jamtracks_sold = ( SELECT COALESCE ( SUM ( jamtracks_sold ) , 0 ) FROM affiliate_quarterly_payments AS aqp WHERE aqp . affiliate_partner_id = affiliate_partners . id ) ,
subscriptions_count = ( SELECT COALESCE ( SUM ( subscriptions_count ) , 0 ) FROM affiliate_quarterly_payments AS aqp WHERE aqp . affiliate_partner_id = affiliate_partners . 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 ) ,
subscriptions_cumulative_earnings_in_cents = ( SELECT COALESCE ( SUM ( subscription_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 ) ,
jamtrack_cumulative_earnings_in_cents = ( SELECT COALESCE ( SUM ( jamtrack_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 ) ,
current_quarter_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 = FALSE ) ,
subscriptions_current_quarter_in_cents = ( SELECT COALESCE ( SUM ( subscription_due_amount_in_cents ) , 0 ) FROM affiliate_quarterly_payments AS aqp WHERE aqp . affiliate_partner_id = affiliate_partners . id AND closed = FALSE ) ,
jamtrack_current_quarter_in_cents = ( SELECT COALESCE ( SUM ( jamtrack_due_amount_in_cents ) , 0 ) FROM affiliate_quarterly_payments AS aqp WHERE aqp . affiliate_partner_id = affiliate_partners . id AND closed = FALSE )
2015-05-28 13:20:14 +00:00
}
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
2025-03-07 22:38:44 +00:00
# Because we now increment_signups, as users sign up, it's possible this row already exists. however, if there were no signups and only visits, there may still not be a row here.
# So we need to insert the rows, and if they already exist, the INSERT will be a no-op, as long as we also update this statement to not fail if the row already exists.
2015-05-28 13:20:14 +00:00
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
2015-05-29 20:30:23 +00:00
[ Date . new ( year , 10 , 1 ) , Date . new ( year , 12 , 31 ) ]
2015-05-28 13:20:14 +00:00
else
raise " invalid quarter #{ quarter } "
end
end
2015-05-29 20:30:23 +00:00
# 1-based month
2015-05-28 13:20:14 +00:00
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
2025-03-23 18:54:53 +00:00
def jamtrack_due_amount_in_cents
total_in_cents = 0
quarters . where ( paid : false , closed : true ) . each do | quarter |
total_in_cents = total_in_cents + quarter . jamtrack_due_amount_in_cents
end
total_in_cents
end
def subscription_due_amount_in_cents
total_in_cents = 0
quarters . where ( paid : false , closed : true ) . each do | quarter |
total_in_cents = total_in_cents + quarter . subscription_due_amount_in_cents
end
total_in_cents
end
2015-05-28 13:20:14 +00:00
def affiliate_query_params
AffiliatePartner :: AFFILIATE_PARAMS + self . id . to_s
end
2015-10-19 22:53:50 +00:00
2021-02-15 04:32:27 +00:00
# def subscribed_user_referrals
# user_referrals.joins(:sales).where("sales.sale_type = ?", Sale::SUBSCRIPTION_SALE)
# end
def subscribed_user_referrals
user_referrals . where ( " first_subscribed_at IS NOT NULL " )
end
# def revenues_from_subscriptions
# subscribed_user_referrals.select("sales.recurly_total_in_cents").inject(0){ | sum, cent | sum += cent } / 100.0
# end
2015-10-19 22:53:50 +00:00
def to_s
display_name
end
2014-04-22 02:14:22 +00:00
end