Admin Command Menu + Admin Financial View

Christopher Vollick created

I've added a new command to show financial information about the user.
But more importantly I've added an infinite admin subsystem which allows
me to go into a user and then run multiple commands on them.

For now it's just these two info commands, but in the future I'd like to
add mutative commands here.

Finally, since I sometimes look up multiple users in a pretty short
timeframe I made the menu open and if I put something into the list that
doesn't parse as one of the actions it instead switches the current user
so I don't have to do "cancel", "customer", and then put the next one
in.

I can just have the session open and put stuff in as needed.

Change summary

forms/admin_financial_info.rb   |  9 +++
forms/admin_menu.rb             | 14 ++++++
forms/admin_payment_methods.rb  | 10 ++++
forms/admin_transaction_list.rb | 10 ++++
lib/admin_command.rb            | 79 +++++++++++++++++++++++++++++++++++
lib/alt_top_up_form.rb          |  5 +
lib/financial_info.rb           | 25 +++++++++++
lib/payment_methods.rb          | 12 ++++
sgx_jmp.rb                      |  7 --
9 files changed, 163 insertions(+), 8 deletions(-)

Detailed changes

forms/admin_financial_info.rb 🔗

@@ -0,0 +1,9 @@
+result!
+title "Customer Financial Info"
+
+field(
+	var: "declines",
+	label: "Declines",
+	description: "out of 2",
+	value: @info.declines.to_s
+)

forms/admin_menu.rb 🔗

@@ -0,0 +1,14 @@
+form!
+title "Menu"
+
+field(
+	var: "action",
+	type: "list-single",
+	open: true,
+	label: "Pick an action",
+	description: "or put a new customer info",
+	options: [
+		{ value: "info", label: "Customer Info" },
+		{ value: "financial", label: "Customer Billing Information" }
+	]
+)

forms/admin_payment_methods.rb 🔗

@@ -0,0 +1,10 @@
+result!
+title "Customer Payment Methods"
+
+unless @payment_methods.empty?
+	field @payment_methods.to_list_single(label: "Credit Cards")
+end
+
+AltTopUpForm::HasBitcoinAddresses.new(@btc_addresses, desc: nil).each do |spec|
+	field spec
+end

forms/admin_transaction_list.rb 🔗

@@ -0,0 +1,10 @@
+result!
+title "Transactions"
+
+table(
+	@transactions,
+	formatted_amount: "Amount",
+	note: "Note",
+	created_at: "Date",
+	transaction_id: "Transaction ID"
+)

lib/admin_command.rb 🔗

@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require_relative "customer_info_form"
+require_relative "financial_info"
+require_relative "form_template"
+
+class AdminCommand
+	def initialize(target_customer)
+		@target_customer = target_customer
+	end
+
+	def start
+		action_info.then { menu }
+	end
+
+	def reply(form)
+		Command.reply { |reply|
+			reply.allowed_actions = [:next]
+			reply.command << form
+		}
+	end
+
+	def menu
+		reply(FormTemplate.render("admin_menu")).then do |response|
+			handle(response.form.field("action").value)
+		end
+	end
+
+	def handle(action)
+		if respond_to?("action_#{action}")
+			send("action_#{action}")
+		else
+			new_context(action)
+		end.then { menu }
+	end
+
+	def new_context(q)
+		CustomerInfoForm.new.parse_something(q).then do |new_customer|
+			if new_customer.respond_to?(:customer_id)
+				AdminCommand.new(new_customer).start
+			else
+				reply(new_customer.form)
+			end
+		end
+	end
+
+	def action_info
+		@target_customer.admin_info.then do |info|
+			reply(info.form)
+		end
+	end
+
+	def action_financial
+		AdminFinancialInfo.for(@target_customer).then do |financial_info|
+			reply(FormTemplate.render(
+				"admin_financial_info",
+				info: financial_info
+			)).then {
+				pay_methods(financial_info)
+			}.then {
+				transactions(financial_info)
+			}
+		end
+	end
+
+	def pay_methods(financial_info)
+		reply(FormTemplate.render(
+			"admin_payment_methods",
+			**financial_info.to_h
+		))
+	end
+
+	def transactions(financial_info)
+		reply(FormTemplate.render(
+			"admin_transaction_list",
+			transactions: financial_info.transactions
+		))
+	end
+end

lib/alt_top_up_form.rb 🔗

@@ -101,8 +101,9 @@ class AltTopUpForm
 	end
 
 	class HasBitcoinAddresses
-		def initialize(addrs)
+		def initialize(addrs, desc: DESCRIPTION)
 			@addrs = addrs
+			@desc = desc
 		end
 
 		DESCRIPTION =
@@ -116,7 +117,7 @@ class AltTopUpForm
 				var: "btc_address",
 				type: "fixed",
 				label: "Bitcoin Addresses",
-				description: DESCRIPTION,
+				description: @desc,
 				value: @addrs
 			)
 		end

lib/financial_info.rb 🔗

@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require "value_semantics/monkey_patched"
+
+class AdminFinancialInfo
+	value_semantics do
+		transactions ArrayOf(CustomerFinancials::TransactionInfo)
+		declines Integer
+		btc_addresses ArrayOf(String)
+		payment_methods PaymentMethods
+	end
+
+	def self.for(customer)
+		EMPromise.all([
+			customer.transactions, customer.declines,
+			customer.payment_methods, customer.btc_addresses
+		]).then do |transactions, declines, payment_methods, btc_addresses|
+			new(
+				transactions: transactions,
+				declines: declines || 0,
+				payment_methods: payment_methods, btc_addresses: btc_addresses
+			)
+		end
+	end
+end

lib/payment_methods.rb 🔗

@@ -50,7 +50,13 @@ class PaymentMethods
 		false
 	end
 
-	class Empty
+	def to_a
+		@methods
+	end
+
+	class Empty < PaymentMethods
+		def initialize; end
+
 		def default_payment_method; end
 
 		def to_list_single(*)
@@ -60,5 +66,9 @@ class PaymentMethods
 		def empty?
 			true
 		end
+
+		def to_a
+			[]
+		end
 	end
 end

sgx_jmp.rb 🔗

@@ -67,6 +67,7 @@ end
 
 require_relative "lib/polyfill"
 require_relative "lib/alt_top_up_form"
+require_relative "lib/admin_command"
 require_relative "lib/add_bitcoin_address"
 require_relative "lib/backend_sgx"
 require_relative "lib/bwmsgsv2_repo"
@@ -726,11 +727,7 @@ Command.new(
 		}.then { |response|
 			CustomerInfoForm.new.find_customer(response)
 		}.then do |target_customer|
-			target_customer.admin_info.then do |info|
-				Command.finish do |reply|
-					reply.command << info.form
-				end
-			end
+			AdminCommand.new(target_customer).start
 		end
 	end
 }.register(self).then(&CommandList.method(:register))