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