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