~singpolyma/sgx-jmp

d4d9e6c39e41bd83fa5aeba2561328e10b19aab5 — Stephen Paul Weber 1 year, 5 months ago 0db4192 + b1c4110
Merge branch 'block-calls-when-expired'

* block-calls-when-expired:
  Block voice calls when account is expired
  Refactor CallAttempt factory to use registration pattern
4 files changed, 90 insertions(+), 25 deletions(-)

M lib/call_attempt.rb
M lib/call_attempt_repo.rb
A views/inbound/expired.slim
A views/outbound/expired.slim
M lib/call_attempt.rb => lib/call_attempt.rb +82 -23
@@ 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

M lib/call_attempt_repo.rb => lib/call_attempt_repo.rb +2 -2
@@ 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

A views/inbound/expired.slim => views/inbound/expired.slim +3 -0
@@ 0,0 1,3 @@
doctype xml
Response
	Hangup /

A views/outbound/expired.slim => views/outbound/expired.slim +3 -0
@@ 0,0 1,3 @@
doctype xml
Response
	SpeakSentence Your account is expired, please top up soon to keep your number.