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.active? ? "Active" : "Expired"
 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	end
116
117	def self.for(
118		customer,
119		trust_level_repo: TrustLevelRepo.new,
120		call_attempt_repo: CallAttemptRepo.new
121	)
122		PromiseHash.all(
123			jid: customer.jid,
124			customer_id: customer.customer_id,
125			fwd: customer.fwd,
126			info: CustomerInfo.for(customer),
127			api: API.for(customer),
128			call_info: call_info(customer, call_attempt_repo),
129			trust_level: trust_level_repo.find(customer).then(&:to_s)
130		).then(&method(:new))
131	end
132
133	class FakeLowBalance
134		def self.for(_)
135			self
136		end
137
138		def self.notify!
139			EMPromise.resolve(0)
140		end
141	end
142
143	def self.call_info(customer, call_attempt_repo)
144		if customer.registered?
145			call_attempt_repo
146				.find_outbound(customer, "+1", call_id: "dry_run")
147				.then(&:to_s)
148		else
149			EMPromise.resolve("No calling")
150		end
151	end
152
153	def form
154		FormTemplate.render("admin_info", admin_info: self)
155	end
156
157	def tel_link
158		[
159			"https://dashboard.bandwidth.com/portal/r/a",
160			CONFIG[:creds][:account],
161			"numbers/details",
162			info.tel.gsub(/\A\+1/, "")
163		].join("/")
164	end
165end