diff --git a/lib/customer.rb b/lib/customer.rb index 62d5c58e0f8d500a2d77fde60c74f34b9136f044..76bbfb4b0d5fc849abd9347c8cbab895f03055f4 100644 --- a/lib/customer.rb +++ b/lib/customer.rb @@ -84,7 +84,7 @@ class Customer def with_plan(plan_name, **kwargs) self.class.new( @customer_id, @jid, - plan: @plan.with_plan_name(plan_name, **kwargs), + plan: @plan.with(plan_name: plan_name, **kwargs), balance: @balance, tndetails: @tndetails, sgx: @sgx ) end diff --git a/lib/customer_plan.rb b/lib/customer_plan.rb index bacd26640dde3febd4475ccca29a9b00dbd272fa..b63230867b5bc1dfc532ee96ea1b7ae20b680aa9 100644 --- a/lib/customer_plan.rb +++ b/lib/customer_plan.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "forwardable" +require "value_semantics/monkey_patched" require_relative "em" require_relative "plan" @@ -8,21 +9,24 @@ require_relative "plan" class CustomerPlan extend Forwardable - attr_reader :expires_at, :auto_top_up_amount, :monthly_overage_limit, - :parent_customer_id - - def_delegator :@plan, :name, :plan_name - def_delegators :@plan, :currency, :merchant_account, :monthly_price, + def_delegator :plan, :name, :plan_name + def_delegators :plan, :currency, :merchant_account, :monthly_price, :minute_limit, :message_limit - def self.for(customer_id, plan_name: nil, **kwargs) - new(customer_id, plan: plan_name&.then(&Plan.method(:for)), **kwargs) + value_semantics do + customer_id String + plan Anything(), default: nil, coerce: true + expires_at Either(Time, nil), default_generator: -> { Time.now } + auto_top_up_amount Integer, default: 0 + monthly_overage_limit Integer, default: 0 + pending Bool(), default: false + parent_customer_id Either(String, nil), default: nil end def self.default(customer_id, jid) config = CONFIG[:parented_domains][Blather::JID.new(jid).domain] if config - self.for( + new( customer_id, plan_name: config[:plan_name], parent_customer_id: config[:customer_id] @@ -33,7 +37,7 @@ class CustomerPlan end def self.extract(customer_id, **kwargs) - self.for( + new( customer_id, **kwargs.slice( :plan_name, :expires_at, :parent_customer_id, :pending, @@ -42,46 +46,32 @@ class CustomerPlan ) end - def initialize( - customer_id, - plan: nil, - expires_at: Time.now, - auto_top_up_amount: 0, - monthly_overage_limit: 0, - pending: false, parent_customer_id: nil - ) - @customer_id = customer_id - @plan = plan || OpenStruct.new - @expires_at = expires_at - @auto_top_up_amount = auto_top_up_amount || 0 - @monthly_overage_limit = monthly_overage_limit || 0 - @pending = pending - @parent_customer_id = parent_customer_id + def self.coerce_plan(plan_or_name_or_nil) + return OpenStruct.new unless plan_or_name_or_nil + + Plan.for(plan_or_name_or_nil) + end + + def initialize(customer_id=nil, **kwargs) + kwargs[:plan] = kwargs.delete(:plan_name) if kwargs.key?(:plan_name) + super(customer_id ? kwargs.merge(customer_id: customer_id) : kwargs) end def active? - plan_name && @expires_at > Time.now + plan_name && expires_at > Time.now end def status return :active if active? - return :pending if @pending + return :pending if pending :expired end - def with_plan_name(plan_name, **kwargs) - self.class.new( - @customer_id, - plan: Plan.for(plan_name), - expires_at: @expires_at, **kwargs - ) - end - def verify_parent! - return unless @parent_customer_id + return unless parent_customer_id - result = DB.query(<<~SQL, [@parent_customer_id]) + result = DB.query(<<~SQL, [parent_customer_id]) SELECT plan_name FROM customer_plans WHERE customer_id=$1 SQL @@ -93,7 +83,7 @@ class CustomerPlan def save_plan! verify_parent! - DB.exec_defer(<<~SQL, [@customer_id, plan_name, @parent_customer_id]) + DB.exec_defer(<<~SQL, [customer_id, plan_name, parent_customer_id]) INSERT INTO plan_log (customer_id, plan_name, parent_customer_id, date_range) VALUES ( @@ -122,7 +112,7 @@ class CustomerPlan def activate_plan_starting_now verify_parent! - activated = DB.exec(<<~SQL, [@customer_id, plan_name, @parent_customer_id]) + activated = DB.exec(<<~SQL, [customer_id, plan_name, parent_customer_id]) INSERT INTO plan_log (customer_id, plan_name, date_range, parent_customer_id) VALUES ($1, $2, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month'), $3) ON CONFLICT DO NOTHING @@ -130,7 +120,7 @@ class CustomerPlan activated = activated.cmd_tuples.positive? return false unless activated - DB.exec(<<~SQL, [@customer_id]) + DB.exec(<<~SQL, [customer_id]) DELETE FROM plan_log WHERE customer_id=$1 AND date_range << '[now,now]' AND upper(date_range) - lower(date_range) < '2 seconds' SQL @@ -141,22 +131,24 @@ class CustomerPlan end def activation_date - DB.query_one(<<~SQL, @customer_id).then { |r| r[:start_date] } + DB.query_one(<<~SQL, customer_id).then { |r| r[:start_date] } SELECT MIN(LOWER(date_range)) AS start_date FROM plan_log WHERE customer_id = $1; SQL end + protected :customer_id, :plan, :pending, :[] + protected def charge_for_plan(note) - raise "No plan setup" unless @plan + raise "No plan setup" unless plan params = [ - @customer_id, - "#{@customer_id}-bill-#{plan_name}-at-#{Time.now.to_i}", - -@plan.monthly_price, + customer_id, + "#{customer_id}-bill-#{plan_name}-at-#{Time.now.to_i}", + -plan.monthly_price, note ] DB.exec(<<~SQL, params) @@ -167,7 +159,7 @@ protected end def add_one_month_to_current_plan - DB.exec(<<~SQL, [@customer_id]) + DB.exec(<<~SQL, [customer_id]) UPDATE plan_log SET date_range=range_merge( date_range, tsrange( diff --git a/lib/plan.rb b/lib/plan.rb index 2db0dad2b4fae843461047ab60b30e5427e0b81f..46671495f37d7949d7c91375de58133f1d88ad09 100644 --- a/lib/plan.rb +++ b/lib/plan.rb @@ -2,6 +2,8 @@ class Plan def self.for(plan_name) + return plan_name if plan_name.is_a?(Plan) + plan = CONFIG[:plans].find { |p| p[:name] == plan_name } raise "No plan by that name" unless plan