1# frozen_string_literal: true
2
3require "bigdecimal/util"
4require "securerandom"
5require "time"
6require "value_semantics/monkey_patched"
7
8require_relative "../admin_action"
9require_relative "../form_to_h"
10
11class AdminAction
12 class AddTransaction < AdminAction
13 class Command
14 using FormToH
15
16 def self.for(target_customer, reply:)
17 time = DateTime.now.iso8601
18 EMPromise.resolve(
19 new(
20 customer_id: target_customer.customer_id,
21 created_at: time, settled_after: time
22 )
23 ).then { |x|
24 reply.call(x.form).then(&x.method(:create))
25 }
26 end
27
28 def initialize(**bag)
29 @bag = bag
30 end
31
32 def form
33 FormTemplate.render("admin_add_transaction")
34 end
35
36 def create(result)
37 hash = result.form.to_h
38 .reject { |_k, v| v == "nil" }.transform_keys(&:to_sym)
39 hash[:transaction_id] = hash[:transaction_id]
40 .sub("%", SecureRandom.uuid)
41
42 AdminAction::AddTransaction.for(
43 **@bag,
44 **hash
45 )
46 end
47 end
48
49 TransactionExists = Struct.new(:transaction_id) do
50 def to_s
51 "The transaction #{transaction_id} already exists"
52 end
53 end
54
55 TransactionDoesNotExist = Struct.new(:transaction_id) do
56 def to_s
57 "The transaction #{transaction_id} doesn't exist"
58 end
59 end
60
61 def customer_id
62 @attributes[:customer_id]
63 end
64
65 def amount
66 @attributes[:amount].to_d
67 end
68
69 def transaction_id
70 @attributes[:transaction_id]
71 end
72
73 def created_at
74 @attributes[:created_at]
75 end
76
77 def settled_after
78 @attributes[:settled_after]
79 end
80
81 def note
82 @attributes[:note]
83 end
84
85 def bonus_eligible?
86 ["1", "true"].include?(@attributes[:bonus_eligible?])
87 end
88
89 def transaction
90 @transaction ||= Transaction.new(
91 **@attributes.slice(:customer_id, :transaction_id, :amount, :note),
92 created_at: created_at, settled_after: settled_after,
93 bonus_eligible?: bonus_eligible?
94 )
95 end
96
97 def check_forward
98 EMPromise.resolve(nil)
99 .then { check_noop }
100 .then { transaction.exists? }
101 .then { |e|
102 EMPromise.reject(TransactionExists.new(transaction_id)) if e
103 }
104 end
105
106 def check_reverse
107 EMPromise.resolve(nil)
108 .then { check_noop }
109 .then { transaction.exists? }
110 .then { |e|
111 EMPromise.reject(TransactionDoesNotExist.new(transaction_id)) unless e
112 }
113 end
114
115 def to_s
116 "add_transaction(#{customer_id}): #{note} (#{transaction_id}) "\
117 "#{transaction}"
118 end
119
120 def forward
121 transaction.insert.then {
122 self
123 }
124 end
125
126 def reverse
127 transaction.delete.then {
128 self
129 }
130 end
131
132 protected
133
134 def check_noop
135 EMPromise.reject(NoOp.new) if amount.zero?
136 end
137 end
138end