Allow parent plan to apply subaccount discount

Stephen Paul Weber created

Change summary

bin/sim_job            |  3 +
config-schema.dhall    |  1 
config.dhall.sample    |  3 +
lib/customer.rb        |  2 
lib/customer_plan.rb   | 50 +++++++++++++++++++++---------------------
lib/customer_repo.rb   |  9 ++++---
lib/parented_domain.rb | 19 ++++++++++++++++
lib/plan.rb            |  4 +++
8 files changed, 59 insertions(+), 32 deletions(-)

Detailed changes

bin/sim_job 🔗

@@ -27,7 +27,8 @@ SCHEMA = "{
 		minutes: < limited: { included: Natural, price: Natural } | unlimited >,
 		monthly_price : Natural,
 		name : Text,
-		allow_register: Bool
+		allow_register: Bool,
+		subaccount_discount: Natural
 	}
 }"
 

config-schema.dhall 🔗

@@ -43,6 +43,7 @@
           < limited : { included : Natural, price : Natural } | unlimited >
       , monthly_price : Natural
       , name : Text
+      , subaccount_discount : Natural
       }
 , reachability_senders : List Text
 , rev_ai_token : Text

config.dhall.sample 🔗

@@ -57,7 +57,8 @@ in
 				unlimited |
 				limited: { included: Natural, price: Natural }
 			>.unlimited,
-			allow_register = True
+			allow_register = True,
+			subaccount_discount = 0
 		}
 	],
 	sims = {

lib/customer.rb 🔗

@@ -37,7 +37,7 @@ class Customer
 	def self.extract(customer_id, jid, **kwargs)
 		(kwargs[:parent_customer_id] ? ChildCustomer : Customer).new(
 			customer_id, jid,
-			plan: CustomerPlan.extract(customer_id, **kwargs),
+			plan: CustomerPlan.extract(customer_id: customer_id, **kwargs),
 			**kwargs.slice(:balance, :sgx, :tndetails, :feature_flags)
 		)
 	end

lib/customer_plan.rb 🔗

@@ -4,13 +4,14 @@ require "forwardable"
 require "value_semantics/monkey_patched"
 
 require_relative "em"
+require_relative "parented_domain"
 require_relative "plan"
 
 class CustomerPlan
 	extend Forwardable
 
 	def_delegator :plan, :name, :plan_name
-	def_delegators :plan, :currency, :merchant_account, :monthly_price,
+	def_delegators :plan, :currency, :merchant_account,
 	               :minute_limit, :message_limit
 
 	value_semantics do
@@ -21,40 +22,35 @@ class CustomerPlan
 		monthly_overage_limit Integer, default: 0
 		pending               Bool(), default: false
 		parent_customer_id    Either(String, nil), default: nil
+		parent_plan           Anything(), default: nil, coerce: true
 	end
 
-	def self.default(customer_id, jid)
-		config = CONFIG[:parented_domains][Blather::JID.new(jid).domain]
-		if config
-			new(
-				customer_id,
-				plan_name: config[:plan_name],
-				parent_customer_id: config[:customer_id]
-			)
-		else
-			new(customer_id)
+	class << self
+		def default(customer_id, jid)
+			new(customer_id, **ParentedDomain.for(jid)&.plan_kwargs || {})
 		end
-	end
 
-	def self.extract(customer_id, **kwargs)
-		new(
-			customer_id,
-			**kwargs.slice(
-				:plan_name, :expires_at, :parent_customer_id, :pending,
-				:auto_top_up_amount, :monthly_overage_limit
-			)
-		)
-	end
+		def extract(**kwargs)
+			new(**kwargs.slice(
+				*value_semantics.attributes.map(&:name), :plan_name, :parent_plan_name
+			))
+		end
 
-	def self.coerce_plan(plan_or_name_or_nil)
-		return OpenStruct.new unless plan_or_name_or_nil
+		def coerce_plan(plan_or_name_or_nil)
+			return plan_or_name_or_nil if plan_or_name_or_nil.is_a?(OpenStruct)
+			return OpenStruct.new unless plan_or_name_or_nil
 
-		Plan.for(plan_or_name_or_nil)
+			Plan.for(plan_or_name_or_nil)
+		end
+		alias coerce_parent_plan coerce_plan
 	end
 
 	def initialize(customer_id=nil, **kwargs)
 		kwargs, customer_id = customer_id, nil if customer_id.is_a?(Hash)
 		kwargs[:plan] = kwargs.delete(:plan_name) if kwargs.key?(:plan_name)
+		if kwargs.key?(:parent_plan_name)
+			kwargs[:parent_plan] = kwargs.delete(:parent_plan_name)
+		end
 		super(customer_id ? kwargs.merge(customer_id: customer_id) : kwargs)
 	end
 
@@ -69,6 +65,10 @@ class CustomerPlan
 		:expired
 	end
 
+	def monthly_price
+		plan.monthly_price - (parent_plan&.subaccount_discount || 0)
+	end
+
 	def verify_parent!
 		return unless parent_customer_id
 
@@ -149,7 +149,7 @@ protected
 		params = [
 			customer_id,
 			"#{customer_id}-bill-#{plan_name}-at-#{Time.now.to_i}",
-			-plan.monthly_price,
+			-monthly_price,
 			note
 		]
 		DB.exec(<<~SQL, params)

lib/customer_repo.rb 🔗

@@ -179,10 +179,11 @@ protected
 
 	SQL = <<~SQL
 		SELECT
-			COALESCE(balance,0) AS balance, plan_name, expires_at,
-			parent_customer_id, pending
-		FROM customer_plans LEFT JOIN balances USING (customer_id)
-		WHERE customer_id=$1 LIMIT 1
+			COALESCE(balance,0) AS balance, cust.plan_name, cust.expires_at,
+			cust.parent_customer_id, cust.pending, parent.plan_name AS parent_plan_name
+		FROM customer_plans cust LEFT JOIN balances USING (customer_id)
+		LEFT JOIN customer_plans parent ON parent.customer_id=cust.parent_customer_id
+		WHERE cust.customer_id=$1 LIMIT 1
 	SQL
 
 	def tndetails(sgx)

lib/parented_domain.rb 🔗

@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class ParentedDomain
+	def self.for(jid)
+		config = CONFIG[:parented_domains][Blather::JID.new(jid).domain]
+		config ? new(config) : nil
+	end
+
+	def initialize(config)
+		@config = config
+	end
+
+	def plan_kwargs
+		{
+			plan_name: @config[:plan_name],
+			parent_customer_id: @config[:customer_id]
+		}
+	end
+end

lib/plan.rb 🔗

@@ -33,6 +33,10 @@ class Plan
 		BigDecimal(@plan[:monthly_price]) / 10000
 	end
 
+	def subaccount_discount
+		BigDecimal(@plan[:subaccount_discount] || 0) / 10000
+	end
+
 	def allow_register?
 		!!@plan[:allow_register]
 	end