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