customer_plan.rb

  1# frozen_string_literal: true
  2
  3require "forwardable"
  4
  5require_relative "em"
  6require_relative "plan"
  7
  8class CustomerPlan
  9	extend Forwardable
 10
 11	attr_reader :expires_at, :auto_top_up_amount, :monthly_overage_limit
 12
 13	def_delegator :@plan, :name, :plan_name
 14	def_delegators :@plan, :currency, :merchant_account, :monthly_price
 15
 16	def self.for(customer_id, plan_name: nil, **kwargs)
 17		new(customer_id, plan: plan_name&.then(&Plan.method(:for)), **kwargs)
 18	end
 19
 20	def initialize(
 21		customer_id,
 22		plan: nil,
 23		expires_at: Time.now,
 24		auto_top_up_amount: 0,
 25		monthly_overage_limit: 0
 26	)
 27		@customer_id = customer_id
 28		@plan = plan || OpenStruct.new
 29		@expires_at = expires_at
 30		@auto_top_up_amount = auto_top_up_amount || 0
 31		@monthly_overage_limit = monthly_overage_limit || 0
 32	end
 33
 34	def active?
 35		plan_name && @expires_at > Time.now
 36	end
 37
 38	def with_plan_name(plan_name)
 39		self.class.new(
 40			@customer_id,
 41			plan: Plan.for(plan_name),
 42			expires_at: @expires_at
 43		)
 44	end
 45
 46	def bill_plan
 47		EM.promise_fiber do
 48			DB.transaction do
 49				charge_for_plan
 50				add_one_month_to_current_plan unless activate_plan_starting_now
 51			end
 52		end
 53	end
 54
 55	def activate_plan_starting_now
 56		DB.exec(<<~SQL, [@customer_id, plan_name]).cmd_tuples.positive?
 57			INSERT INTO plan_log
 58				(customer_id, plan_name, date_range)
 59			VALUES ($1, $2, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month'))
 60			ON CONFLICT DO NOTHING
 61		SQL
 62	end
 63
 64	def activation_date
 65		dates = DB.query_defer(<<~SQL, [@customer_id])
 66			SELECT
 67				MIN(LOWER(date_range)) AS start_date
 68			FROM plan_log WHERE customer_id = $1;
 69		SQL
 70
 71		dates.then do |r|
 72			r.first["start_date"]
 73		end
 74	end
 75
 76protected
 77
 78	def charge_for_plan
 79		params = [
 80			@customer_id,
 81			"#{@customer_id}-bill-#{plan_name}-at-#{Time.now.to_i}",
 82			-@plan.monthly_price
 83		]
 84		DB.exec(<<~SQL, params)
 85			INSERT INTO transactions
 86				(customer_id, transaction_id, created_at, amount)
 87			VALUES ($1, $2, LOCALTIMESTAMP, $3)
 88		SQL
 89	end
 90
 91	def add_one_month_to_current_plan
 92		DB.exec(<<~SQL, [@customer_id])
 93			UPDATE plan_log SET date_range=range_merge(
 94				date_range,
 95				tsrange(
 96					LOCALTIMESTAMP,
 97					GREATEST(upper(date_range), LOCALTIMESTAMP) + '1 month'
 98				)
 99			)
100			WHERE
101				customer_id=$1 AND
102				date_range && tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month')
103		SQL
104	end
105end