Merge branch 'block-calls-when-expired'

Stephen Paul Weber created

* block-calls-when-expired:
  Block voice calls when account is expired
  Refactor CallAttempt factory to use registration pattern

Change summary

lib/call_attempt.rb         | 105 ++++++++++++++++++++++++++++++--------
lib/call_attempt_repo.rb    |   4 
views/inbound/expired.slim  |   3 +
views/outbound/expired.slim |   3 +
4 files changed, 90 insertions(+), 25 deletions(-)

Detailed changes

lib/call_attempt.rb 🔗

@@ -6,33 +6,32 @@ require_relative "tts_template"
 require_relative "low_balance"
 
 class CallAttempt
-	def self.for(customer, rate, usage, supported, **kwargs)
+	def self.for(customer:, usage:, **kwargs)
 		credit = [customer.minute_limit.to_d - usage, 0].max + customer.balance
-		if !supported
-			Unsupported.new(**kwargs.slice(:direction))
-		elsif credit < rate * 10
-			NoBalance.for(customer, rate, usage, supported, **kwargs)
-		else
-			for_ask_or_go(customer, rate, usage, credit, **kwargs)
+		@kinds.each do |kind|
+			ca = kind.call(
+				customer: customer, usage: usage, credit: credit,
+				**kwargs.merge(limits(customer, usage, credit, **kwargs))
+			)
+			return ca if ca
 		end
+
+		raise "No CallAttempt matched"
 	end
 
-	def self.for_ask_or_go(customer, rate, usage, credit, digits: nil, **kwargs)
-		kwargs.merge!(
-			customer_id: customer.customer_id,
-			limit_remaining: limit_remaining(customer, usage, rate),
+	def self.limits(customer, usage, credit, rate:, **)
+		return {} unless customer && usage && rate
+
+		can_use = customer.minute_limit.to_d + customer.monthly_overage_limit
+		{
+			limit_remaining: ([can_use - usage, 0].max / rate).to_i,
 			max_minutes: (credit / rate).to_i
-		)
-		if digits != "1" && limit_remaining(customer, usage, rate) < 10
-			AtLimit.new(**kwargs)
-		else
-			new(**kwargs)
-		end
+		}
 	end
 
-	def self.limit_remaining(customer, usage, rate)
-		can_use = customer.minute_limit.to_d + customer.monthly_overage_limit
-		([can_use - usage, 0].max / rate).to_i
+	def self.register(&maybe_mk)
+		@kinds ||= []
+		@kinds << maybe_mk
 	end
 
 	value_semantics do
@@ -67,7 +66,43 @@ class CallAttempt
 		as_json.to_json(*args)
 	end
 
+	class Expired
+		CallAttempt.register do |customer:, direction:, **|
+			new(direction: direction) if customer.plan_name && !customer.active?
+		end
+
+		value_semantics do
+			direction Either(:inbound, :outbound)
+		end
+
+		def view
+			"#{direction}/expired"
+		end
+
+		def tts
+			TTSTemplate.new(view).tts(self)
+		end
+
+		def to_render
+			[view]
+		end
+
+		def create_call(*); end
+
+		def as_json(*)
+			{ tts: tts }
+		end
+
+		def to_json(*args)
+			as_json.to_json(*args)
+		end
+	end
+
 	class Unsupported
+		CallAttempt.register do |supported:, direction:, **|
+			new(direction: direction) unless supported
+		end
+
 		value_semantics do
 			direction Either(:inbound, :outbound)
 		end
@@ -96,12 +131,16 @@ class CallAttempt
 	end
 
 	class NoBalance
-		def self.for(customer, rate, usage, supported, direction:, **kwargs)
+		CallAttempt.register do |credit:, rate:, **kwargs|
+			self.for(rate: rate, **kwargs) if credit < rate * 10
+		end
+
+		def self.for(customer:, direction:, **kwargs)
 			LowBalance.for(customer).then(&:notify!).then do |amount|
 				if amount&.positive?
 					CallAttempt.for(
-						customer.with_balance(customer.balance + amount),
-						rate, usage, supported, direction: direction, **kwargs
+						customer: customer.with_balance(customer.balance + amount),
+						**kwargs.merge(direction: direction)
 					)
 				else
 					NoBalance.new(balance: customer.balance, direction: direction)
@@ -148,6 +187,18 @@ class CallAttempt
 			max_minutes Integer
 		end
 
+		CallAttempt.register do |digits: nil, limit_remaining:, customer:, **kwargs|
+			if digits != "1" && limit_remaining < 10
+				new(
+					**kwargs
+						.merge(
+							limit_remaining: limit_remaining,
+							customer_id: customer.customer_id
+						).slice(*value_semantics.attributes.map(&:name))
+				)
+			end
+		end
+
 		def view
 			"#{direction}/at_limit"
 		end
@@ -179,4 +230,12 @@ class CallAttempt
 			as_json.to_json(*args)
 		end
 	end
+
+	register do |**kwargs|
+		new(
+			**kwargs
+				.merge(customer_id: customer.customer_id)
+				.slice(*value_semantics.attributes.map(&:name))
+		)
+	end
 end

lib/call_attempt_repo.rb 🔗

@@ -58,8 +58,8 @@ protected
 	def find(customer, other_tel, direction:, **kwargs)
 		find_all(customer, other_tel, direction).then do |(rate, usage, tl, c)|
 			CallAttempt.for(
-				customer, rate, usage,
-				rate && tl.support_call?(rate, c || 0),
+				customer: customer, rate: rate, usage: usage,
+				supported: rate && tl.support_call?(rate, c || 0),
 				direction: direction, **kwargs
 			)
 		end