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