1# frozen_string_literal: true
2
3require "bigdecimal"
4require "forwardable"
5require "relative_time"
6require "value_semantics/monkey_patched"
7
8require_relative "call_attempt_repo"
9require_relative "customer"
10require_relative "customer_plan"
11require_relative "form_template"
12require_relative "promise_hash"
13require_relative "proxied_jid"
14require_relative "subaccount"
15
16class PlanInfo
17 extend Forwardable
18
19 def_delegators :customer, :expires_at, :auto_top_up_amount
20
21 def self.for(customer)
22 return EMPromise.resolve(NoPlan.new) unless customer&.plan_name
23
24 PromiseHash.all(
25 customer: customer,
26 start_date: customer.activation_date,
27 calling_charges_this_month: customer.calling_charges_this_month,
28 billing_customer_id: customer.billing_customer_id
29 ).then(method(:new))
30 end
31
32 class NoPlan
33 def template
34 FormTemplate.new("")
35 end
36
37 def admin_template
38 FormTemplate.new("")
39 end
40
41 def status
42 "Transitional"
43 end
44 end
45
46 value_semantics do
47 method_missing :customer, Customer
48 start_date Time
49 calling_charges_this_month BigDecimal
50 billing_customer_id String
51 end
52
53 def template
54 FormTemplate.for("plan_info", plan_info: self)
55 end
56
57 def admin_template
58 FormTemplate.for("admin_plan_info", plan_info: self)
59 end
60
61 def monthly_price
62 "$%.4f / month" % customer.monthly_price
63 end
64
65 def relative_start_date
66 RelativeTime.in_words(start_date)
67 end
68
69 def formatted_start_date
70 start_date.strftime("%Y-%m-%d %H:%M:%S")
71 end
72
73 def currency
74 (customer.currency || "No Currency").to_s
75 end
76
77 def status
78 customer.status.to_s.capitalize
79 end
80
81 def remaining_included_calling_credit
82 [customer.minute_limit.to_d - calling_charges_this_month, 0].max
83 end
84end
85
86class CustomerInfo
87 value_semantics do
88 plan_info Either(PlanInfo, PlanInfo::NoPlan)
89 tel Either(String, nil)
90 balance BigDecimal
91 cnam Either(String, nil)
92 end
93
94 def self.for(customer)
95 PromiseHash.all(
96 plan_info: PlanInfo.for(customer),
97 tel: customer.registered? ? customer.registered?.phone : nil,
98 balance: customer.balance,
99 cnam: customer.tndetails.dig(:features, :lidb, :subscriber_information)
100 ).then(&method(:new))
101 end
102
103 def form
104 FormTemplate.render("customer_info", info: self)
105 end
106end
107
108class AdminInfo
109 value_semantics do
110 jid ProxiedJID, coerce: ProxiedJID.method(:new)
111 customer_id String
112 fwd Either(CustomerFwd, nil)
113 info CustomerInfo
114 call_info String
115 trust_level String
116 backend_jid String
117 subaccounts ArrayOf(::Subaccount), default: []
118 end
119
120 def self.for(
121 customer,
122 trust_level_repo: TrustLevelRepo.new,
123 call_attempt_repo: CallAttemptRepo.new,
124 backend_repo: TrivialBackendSgxRepo.new
125 )
126 PromiseHash.all(
127 jid: customer.jid, customer_id: customer.customer_id,
128 fwd: customer.fwd,
129 info: CustomerInfo.for(customer),
130 call_info: call_info(customer, call_attempt_repo),
131 trust_level: trust_level_repo.find(customer).then(&:to_s),
132 backend_jid: backend_repo.get(customer.customer_id)
133 .then(&:from_jid).then(&:to_s),
134 subaccounts: Subaccount.get_subaccounts(customer.billing_customer_id)
135 ).then(&method(:new))
136 end
137
138 class FakeLowBalance
139 def self.for(_)
140 self
141 end
142
143 def self.notify!
144 EMPromise.resolve(0)
145 end
146 end
147
148 def self.call_info(customer, call_attempt_repo)
149 if customer.registered?
150 call_attempt_repo
151 .find_outbound(customer, "+1", call_id: "dry_run")
152 .then(&:to_s)
153 else
154 EMPromise.resolve("No calling")
155 end
156 end
157
158 def form
159 FormTemplate.render("admin_info", admin_info: self)
160 end
161
162 def tel_link
163 [
164 "https://dashboard.bandwidth.com/portal/r/a",
165 CONFIG[:creds][:account],
166 "numbers/details",
167 info.tel.gsub(/\A\+1/, "")
168 ].join("/")
169 end
170
171 def support_link
172 CONFIG[:support_link].call(backend_jid)
173 end
174end