customer_info.rb

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