1# frozen_string_literal: true
 2
 3require "bigdecimal"
 4require "time"
 5require "value_semantics/monkey_patched"
 6
 7class Transaction
 8	value_semantics do
 9		customer_id String
10		transaction_id String
11		created_at Time, coerce: ->(x) { Time.parse(x.to_s) },
12		                 default_generator: -> { Time.now }
13		settled_after Time, coerce: ->(x) { Time.parse(x.to_s) },
14		                    default_generator: -> { Time.now }
15		amount BigDecimal, coerce: ->(x) { BigDecimal(x, 4) }
16		note String
17		bonus_eligible? Bool(), default: true
18		ignore_duplicate Bool(), default: false
19	end
20
21	def insert
22		EM.promise_fiber do
23			DB.transaction do
24				insert_tx
25				insert_bonus
26			end
27		end
28	end
29
30	def delete
31		ids = [@transaction_id, "bonus_for_#{@transaction_id}"]
32		DB.query_defer(<<~SQL, ids)
33			DELETE FROM transactions WHERE transaction_id IN ($1, $2)
34		SQL
35	end
36
37	def exists?
38		DB.query_one(<<~SQL, @transaction_id).then { |r| r[:count] == 1 }
39			SELECT COUNT(1) FROM transactions
40			WHERE transaction_id = $1
41		SQL
42	end
43
44	def total
45		amount + bonus
46	end
47
48	def bonus
49		return BigDecimal(0) unless bonus_eligible? && amount > 15
50
51		amount *
52			case amount
53			when (15..29.99)
54				0.01
55			when (30..139.99)
56				0.03
57			else
58				0.05
59			end
60	end
61
62	def to_s
63		plus = " + #{'%.4f' % bonus} bonus"
64		"$#{'%.2f' % amount}#{plus if bonus.positive?}"
65	end
66
67	def insert_tx
68		params = [
69			@customer_id, @transaction_id, @created_at, @settled_after, @amount, @note
70		]
71		DB.exec(<<~SQL, params)
72			INSERT INTO transactions
73				(customer_id, transaction_id, created_at, settled_after, amount, note)
74			VALUES
75				($1, $2, $3, $4, $5, $6)
76			#{ignore_duplicate ? 'ON CONFLICT (transaction_id) DO NOTHING' : ''}
77		SQL
78	end
79
80protected
81
82	def insert_bonus
83		return if bonus <= 0
84
85		params = [
86			@customer_id, "bonus_for_#{@transaction_id}", @created_at,
87			@settled_after, bonus, "#{@note} bonus"
88		]
89		DB.exec(<<~SQL, params)
90			INSERT INTO transactions
91				(customer_id, transaction_id, created_at, settled_after, amount, note)
92			VALUES
93				($1, $2, $3, $4, $5, $6)
94		SQL
95	end
96end