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