Skip to content

Commit

Permalink
feat: Bill anniversary subscriptions (#366)
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent-pochet authored Aug 8, 2022
1 parent 907acff commit bd3b211
Show file tree
Hide file tree
Showing 2 changed files with 261 additions and 67 deletions.
128 changes: 101 additions & 27 deletions app/services/billing_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,41 +27,115 @@ def call

private

# Retrieve list of subscription that should be billed today
def today
@today ||= Time.current
end

# NOTE: Retrieve list of subscriptions that should be billed today
def billable_subscriptions
sql = []
today = Time.zone.now

return Subscription.none unless (today.day == 1 || today.monday?)
# NOTE: Calendar subscriptions

# For weekly interval we send invoices on Monday
if today.monday?
sql << Subscription.active.joins(:plan)
.merge(Plan.weekly)
.select(:id).to_sql
end
# NOTE: For weekly interval we send invoices on Monday
sql << weekly_calendar if today.monday?

if today.day == 1
# Billed monthly
sql << Subscription.active.joins(:plan)
.merge(Plan.monthly)
.select(:id).to_sql

# Bill charges monthly for yearly plans
sql << Subscription.active.joins(:plan)
.merge(Plan.yearly)
.merge(Plan.where(bill_charges_monthly: true))
.select(:id).to_sql

# We are on the first day of the year
if today.month == 1
# Billed yearly
sql << Subscription.active.joins(:plan)
.merge(Plan.yearly)
.select(:id).to_sql
end
# NOTE: Billed monthly
sql << monthly_calendar

# NOTE: Bill charges monthly for yearly plans
sql << yearly_with_monthly_charges_calendar

# NOTE: Billed yearly and we are on the first day of the year
sql << yearly_calendar if today.month == 1
end

# NOTE: Anniversary subscriptions
sql << weekly_anniversary
sql << monthly_anniversary
sql << yearly_with_monthly_charges_anniversary
sql << yearly_anniversary

Subscription.where("id in (#{sql.join(' UNION ')})")
end

def weekly_calendar
Subscription.active.joins(:plan)
.calendar
.merge(Plan.weekly)
.select(:id).to_sql
end

def monthly_calendar
Subscription.active.joins(:plan)
.calendar
.merge(Plan.monthly)
.select(:id).to_sql
end

def yearly_with_monthly_charges_calendar
Subscription.active.joins(:plan)
.calendar
.merge(Plan.yearly.where(bill_charges_monthly: true))
.select(:id).to_sql
end

def yearly_calendar
Subscription.active.joins(:plan)
.calendar
.merge(Plan.yearly)
.select(:id).to_sql
end

def weekly_anniversary
Subscription.active.joins(:plan)
.anniversary
.merge(Plan.weekly)
.where('EXTRACT(ISODOW FROM subscriptions.subscription_date) = ?', today.wday)
.select(:id).to_sql
end

def monthly_anniversary
days = [today.day]

# If today is the last day of the month and month count less than 31 days,
# we need to take all days up to 31 into account
((today.day + 1)..31).each { |day| days << day } if today.day == today.end_of_month.day

Subscription.active.joins(:plan)
.anniversary
.merge(Plan.monthly)
.where('DATE_PART(\'day\', subscriptions.subscription_date) IN (?)', days)
.select(:id).to_sql
end

def yearly_anniversary
# Billed yearly
days = [today.day]

# If we are not in leap year and we are on 28/02 take 29/02 into account
days << 29 if !Date.leap?(today.year) && today.day == 28 && today.month == 2

Subscription.active.joins(:plan)
.anniversary
.merge(Plan.yearly)
.where('DATE_PART(\'month\', subscriptions.subscription_date) = ?', today.month)
.where('DATE_PART(\'day\', subscriptions.subscription_date) IN (?)', days)
.select(:id).to_sql
end

def yearly_with_monthly_charges_anniversary
days = [today.day]

# If today is the last day of the month and month count less than 31 days,
# we need to take all days up to 31 into account
((today.day + 1)..31).each { |day| days << day } if today.day == today.end_of_month.day

Subscription.active.joins(:plan)
.anniversary
.merge(Plan.yearly.where(bill_charges_monthly: true))
.where('DATE_PART(\'day\', subscriptions.subscription_date) IN (?)', days)
.select(:id).to_sql
end
end
Loading

0 comments on commit bd3b211

Please sign in to comment.