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