customer_plan.rb

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