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