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