auctify 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +229 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/config/auctify_manifest.js +1 -0
  6. data/app/assets/javascripts/folio/console/main_app.coffee +1 -0
  7. data/app/assets/stylesheets/auctify/application.css +15 -0
  8. data/app/assets/stylesheets/auctify/bidder_registrations.css +4 -0
  9. data/app/assets/stylesheets/auctify/bids.css +4 -0
  10. data/app/assets/stylesheets/auctify/sales.css +4 -0
  11. data/app/assets/stylesheets/auctify/sales_packs.css +4 -0
  12. data/app/assets/stylesheets/folio/console/_main_app.sass +1 -0
  13. data/app/assets/stylesheets/scaffold.css +80 -0
  14. data/app/cells/folio/console/atoms/previews/main_app.coffee +1 -0
  15. data/app/concerns/auctify/behavior/base.rb +20 -0
  16. data/app/concerns/auctify/behavior/buyer.rb +16 -0
  17. data/app/concerns/auctify/behavior/item.rb +27 -0
  18. data/app/concerns/auctify/behavior/seller.rb +36 -0
  19. data/app/concerns/auctify/sale/auction_callbacks.rb +44 -0
  20. data/app/controllers/auctify/api/v1/auctions_controller.rb +61 -0
  21. data/app/controllers/auctify/api/v1/base_controller.rb +22 -0
  22. data/app/controllers/auctify/api/v1/console/bids_controller.rb +37 -0
  23. data/app/controllers/auctify/application_controller.rb +8 -0
  24. data/app/controllers/auctify/bidder_registrations_controller.rb +65 -0
  25. data/app/controllers/auctify/bids_controller.rb +64 -0
  26. data/app/controllers/auctify/sales_controller.rb +75 -0
  27. data/app/controllers/auctify/sales_packs_controller.rb +75 -0
  28. data/app/helpers/auctify/application_helper.rb +10 -0
  29. data/app/helpers/auctify/bidder_registrations_helper.rb +6 -0
  30. data/app/helpers/auctify/bids_helper.rb +6 -0
  31. data/app/helpers/auctify/sales_helper.rb +15 -0
  32. data/app/helpers/auctify/sales_packs_helper.rb +6 -0
  33. data/app/jobs/auctify/application_job.rb +6 -0
  34. data/app/jobs/auctify/bidding_closer_job.rb +23 -0
  35. data/app/jobs/auctify/bidding_is_close_to_end_notifier_job.rb +29 -0
  36. data/app/mailers/auctify/application_mailer.rb +8 -0
  37. data/app/models/auctify/application_record.rb +14 -0
  38. data/app/models/auctify/behaviors.rb +27 -0
  39. data/app/models/auctify/bid.rb +72 -0
  40. data/app/models/auctify/bidder_registration.rb +84 -0
  41. data/app/models/auctify/sale/auction.rb +404 -0
  42. data/app/models/auctify/sale/base.rb +210 -0
  43. data/app/models/auctify/sale/retail.rb +97 -0
  44. data/app/models/auctify/sales_pack.rb +101 -0
  45. data/app/serializers/auctify/sale/auction_serializer.rb +46 -0
  46. data/app/services/auctify/bids_appender.rb +241 -0
  47. data/app/services/auctify/service_base.rb +78 -0
  48. data/app/types/auctify/minimal_bids_ladder_type.rb +77 -0
  49. data/app/views/auctify/api/v1/console/bids/index.html.erb +1 -0
  50. data/app/views/auctify/api/v1/console/bids/show.html.erb +1 -0
  51. data/app/views/auctify/bidder_registrations/_form.html.erb +27 -0
  52. data/app/views/auctify/bidder_registrations/edit.html.erb +6 -0
  53. data/app/views/auctify/bidder_registrations/index.html.erb +33 -0
  54. data/app/views/auctify/bidder_registrations/new.html.erb +5 -0
  55. data/app/views/auctify/bidder_registrations/show.html.erb +24 -0
  56. data/app/views/auctify/bids/_form.html.erb +32 -0
  57. data/app/views/auctify/bids/edit.html.erb +6 -0
  58. data/app/views/auctify/bids/index.html.erb +31 -0
  59. data/app/views/auctify/bids/new.html.erb +5 -0
  60. data/app/views/auctify/bids/show.html.erb +19 -0
  61. data/app/views/auctify/sales/_form.html.erb +32 -0
  62. data/app/views/auctify/sales/edit.html.erb +6 -0
  63. data/app/views/auctify/sales/index.html.erb +36 -0
  64. data/app/views/auctify/sales/new.html.erb +5 -0
  65. data/app/views/auctify/sales/show.html.erb +19 -0
  66. data/app/views/auctify/sales_packs/_form.html.erb +57 -0
  67. data/app/views/auctify/sales_packs/edit.html.erb +6 -0
  68. data/app/views/auctify/sales_packs/index.html.erb +40 -0
  69. data/app/views/auctify/sales_packs/new.html.erb +5 -0
  70. data/app/views/auctify/sales_packs/show.html.erb +44 -0
  71. data/app/views/layouts/auctify/application.html.erb +15 -0
  72. data/config/locales/auctify.cs.yml +121 -0
  73. data/config/locales/default.cs.yml +220 -0
  74. data/config/locales/default.en.yml +215 -0
  75. data/config/routes.rb +23 -0
  76. data/db/migrate/20210212163000_create_auctify_sales.rb +16 -0
  77. data/db/migrate/20210216151350_add_type_to_auctify_sales.rb +7 -0
  78. data/db/migrate/20210217101246_add_state_to_auctify_sales.rb +7 -0
  79. data/db/migrate/20210217110819_add_prices_to_auctify_sales.rb +9 -0
  80. data/db/migrate/20210222103031_add_published_at_to_auctify_sale.rb +7 -0
  81. data/db/migrate/20210223090240_create_auctify_bidder_registrations.rb +15 -0
  82. data/db/migrate/20210223152139_create_auctify_bids.rb +13 -0
  83. data/db/migrate/20210225103227_add_bid_steps_ladder_to_auctify_sales.rb +7 -0
  84. data/db/migrate/20210412114319_add_reserve_price_to_auctify_sales.rb +7 -0
  85. data/db/migrate/20210416140603_create_auctify_sales_packs.rb +17 -0
  86. data/db/migrate/20210416141111_add_pack_reference_to_auctify_sales.rb +7 -0
  87. data/db/migrate/20210419075003_add_ends_at_to_auctify_sales.rb +7 -0
  88. data/db/migrate/20210419082800_add_position_to_auctify_sales.rb +8 -0
  89. data/db/migrate/20210419083321_add_indexes.rb +12 -0
  90. data/db/migrate/20210421093652_add_number_to_auctify_sales.rb +7 -0
  91. data/db/migrate/20210423071326_removing_polymorphic_assoc_sale_to_item.rb +7 -0
  92. data/db/migrate/20210423071534_add_currently_ends_at_to_auctify_sales.rb +7 -0
  93. data/db/migrate/20210427121353_change_column_published_on_auctify_sale.rb +15 -0
  94. data/db/migrate/20210427122315_add_featured_to_auctify_sales.rb +7 -0
  95. data/db/migrate/20210428045651_add_sales_bool_indices.rb +9 -0
  96. data/db/migrate/20210428064615_allow_seller_to_be_nil.rb +13 -0
  97. data/db/migrate/20210429051508_add_sales_pack_start_and_end.rb +15 -0
  98. data/db/migrate/20210430080419_add_sales_slug.rb +11 -0
  99. data/db/migrate/20210503095704_add_cancelled_to_auctify_bids.rb +7 -0
  100. data/db/migrate/20210504072041_add_commission_and_contract_to_auctify_sales.rb +8 -0
  101. data/db/migrate/20210506064100_add_winner_to_auctify_sales.rb +7 -0
  102. data/db/migrate/20210506072438_add_bids_count_to_auctify_sales.rb +7 -0
  103. data/db/migrate/20210507085845_rename_bids_count_to_applied_bids_count.rb +7 -0
  104. data/db/migrate/20210510063146_add_dont_confirm_bids_to_registrations.rb +7 -0
  105. data/db/migrate/20210511081556_add_index_on_cancelled_on.rb +7 -0
  106. data/db/migrate/20210512162942_add_sold_at_to_auctify_sales.rb +7 -0
  107. data/db/migrate/20210607113440_add_commission_in_percent_to_sales_packs.rb +7 -0
  108. data/db/migrate/20210617062509_add_index_to_auctify_sales_currently_ends_at.rb +7 -0
  109. data/db/migrate/20210625125732_add_current_winner_to_auctify_sales.rb +7 -0
  110. data/lib/auctify.rb +7 -0
  111. data/lib/auctify/configuration.rb +43 -0
  112. data/lib/auctify/engine.rb +25 -0
  113. data/lib/auctify/version.rb +5 -0
  114. data/lib/tasks/auctify_tasks.rake +23 -0
  115. metadata +442 -0
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auctify
4
+ class BidsAppender < ServiceBase
5
+ attr_reader :auction, :bid
6
+
7
+ def initialize(auction:, bid: nil)
8
+ super()
9
+
10
+ @auction = auction
11
+ @bid = bid
12
+ end
13
+
14
+ def build_result
15
+ self.current_price = auction.current_price
16
+ @updated_win_bid = nil
17
+
18
+ if bid
19
+ set_price_for_limit_bid
20
+
21
+ if approved_bid?
22
+ solve_winner(winning_bid, bid)
23
+ append_bids!
24
+ update_auction! unless failed?
25
+ else
26
+ fail!
27
+ end
28
+ end
29
+
30
+ @result = result_struct
31
+ end
32
+
33
+ private
34
+ attr_accessor :current_price
35
+
36
+ def result_struct
37
+ OpenStruct.new(
38
+ current_price: current_price,
39
+ current_minimal_bid: new_current_minimal_bid,
40
+ winning_bid: winning_bid,
41
+ winner: winner,
42
+ won_price: (overcame_reserve_price? ? current_price : nil)
43
+ )
44
+ end
45
+
46
+ def set_price_for_limit_bid
47
+ return unless bid.price.blank? && bid.with_limit?
48
+
49
+ if changing_own_limit?
50
+ bid.price = winning_bid.price
51
+ elsif bid.max_price <= new_current_minimal_bid
52
+ bid.price = bid.max_price
53
+ else
54
+ bid.price = new_current_minimal_bid
55
+ end
56
+ end
57
+
58
+ def approved_bid?
59
+ @approved_bid ||= begin
60
+ check_bidder
61
+ changing_own_limit? ? check_max_price_increasing : check_price_minimum
62
+ check_same_bidder
63
+ check_auction_state
64
+
65
+ errors.add_from_hash(bid.errors.to_hash)
66
+
67
+ errors.empty?
68
+ end
69
+ end
70
+
71
+ def append_bids!
72
+ fail! unless bid.save
73
+
74
+ # saving last, so it will still be winning, even if both have same price
75
+ if @updated_win_bid.present?
76
+ fail! unless @updated_win_bid.save
77
+ end
78
+ end
79
+
80
+ def update_auction!
81
+ @winning_bid = nil
82
+ @bids = bids.reload
83
+
84
+
85
+ fail! unless auction.succesfull_bid!(price: new_current_price,
86
+ winner: winner,
87
+ time: bid.reload.created_at)
88
+ self.current_price = auction.current_price
89
+ end
90
+
91
+ def new_current_price
92
+ winning_bid.price
93
+ end
94
+
95
+ def new_current_minimal_bid
96
+ return current_price if first_bid?
97
+
98
+ increase_price(current_price)
99
+ end
100
+
101
+ def winning_bid
102
+ @winning_bid ||= bids.first
103
+ end
104
+
105
+ def winner
106
+ (overcame_reserve_price? ? winning_bid.bidder : nil)
107
+ end
108
+
109
+ def overcame_reserve_price?
110
+ return false if winning_bid.blank?
111
+ return false if auction.reserve_price.present? && auction.current_price < auction.reserve_price
112
+
113
+ true
114
+ end
115
+
116
+ def first_bid?
117
+ bids.empty?
118
+ end
119
+
120
+ def bids
121
+ @bids ||= auction.ordered_applied_bids
122
+ end
123
+
124
+ def check_bidder
125
+ unless bid.registration&.auction == auction
126
+ bid.errors.add(:auction, :bidder_is_not_registered_for_this_auction)
127
+ end
128
+ unless auction.bidding_allowed_for?(bid.bidder)
129
+ bid.errors.add(:bidder, :you_are_not_allowed_to_bid)
130
+ end
131
+ end
132
+
133
+ def check_max_price_increasing
134
+ bid.errors.add(:bidder, :you_can_only_increase_your_max_price) if bid.max_price.present? && (bid.max_price.to_i <= winning_bid.max_price.to_i)
135
+ end
136
+
137
+ def check_price_minimum
138
+ if bid.price < new_current_minimal_bid
139
+ att = bid.with_limit? ? :max_price : :price
140
+ bid.errors.add(att,
141
+ :price_is_bellow_minimal_bid,
142
+ minimal_bid: ActionController::Base.helpers.number_to_currency(new_current_minimal_bid, precision: 0))
143
+ end
144
+ end
145
+
146
+ def check_same_bidder
147
+ return if overbidding_yourself_allowed? || changing_own_limit?
148
+
149
+ if winning_bid.present? && same_bidder?(winning_bid, bid)
150
+ bid.errors.add(:bidder, :you_cannot_overbid_yourself)
151
+ end
152
+ end
153
+
154
+ def check_auction_state
155
+ # comparing time with seconds precision, use `.to_i`
156
+ return if auction.in_sale? && bid.created_at.to_i <= auction.currently_ends_at.to_i
157
+
158
+ bid.errors.add(:auction, :auction_is_not_accepting_bids_now)
159
+ end
160
+
161
+ def solve_winner(winning_bid, new_bid)
162
+ return if winning_bid.blank? || changing_own_limit?
163
+
164
+ solve_limits_fight(winning_bid, new_bid) if new_bid.with_limit? && winning_bid.with_limit?
165
+ increase_bid_price(winning_bid, new_bid) if new_bid.with_limit? && !winning_bid.with_limit?
166
+ duplicate_increased_win_bid(winning_bid, new_bid) if !new_bid.with_limit? && winning_bid.with_limit?
167
+ # do nothing, all is solved already if !bid.with_limit? && !winning_bid.with_limit?
168
+ end
169
+
170
+ def solve_limits_fight(winning_bid, new_bid)
171
+ if winning_bid.max_price < new_bid.max_price
172
+ update_winning_bid_to(winning_bid.max_price)
173
+ new_bid.price = [new_bid.price, increase_price_to(overcome: winning_bid.max_price, ceil: new_bid.max_price)].max
174
+ else
175
+ new_bid.price = new_bid.max_price
176
+ update_winning_bid_to(increase_price_to(overcome: new_bid.max_price, ceil: winning_bid.max_price))
177
+ end
178
+ end
179
+
180
+ def increase_bid_price(winning_bid, new_bid)
181
+ if winning_bid.price < new_bid.max_price
182
+ new_bid.price = [new_bid.price, increase_price_to(overcome: winning_bid.price, ceil: new_bid.max_price)].max
183
+ else
184
+ new_bid.price = new_bid.max_price
185
+ end
186
+ end
187
+
188
+ def duplicate_increased_win_bid(winning_bid, new_bid)
189
+ if winning_bid.max_price < new_bid.price
190
+ update_winning_bid_to(winning_bid.max_price)
191
+ elsif overbidding_yourself_allowed? && same_bidder?(winning_bid, new_bid)
192
+ update_winning_bid_to(new_bid.price, force_if_equal: true)
193
+ else
194
+ update_winning_bid_to(increase_price_to(overcome: new_bid.price, ceil: winning_bid.max_price))
195
+ end
196
+ end
197
+
198
+ def update_winning_bid_to(price, force_if_equal: false)
199
+ return if price < winning_bid.price
200
+ return if price == winning_bid.price && !force_if_equal
201
+
202
+ @updated_win_bid = winning_bid.dup
203
+ @updated_win_bid.price = [price, winning_bid.max_price].min
204
+ end
205
+
206
+ def increase_price(price)
207
+ return price + 1 if bid_steps_ladder.blank?
208
+
209
+ _range, increase_step = bid_steps_ladder.detect { |range, step| range.cover?(price) }
210
+ price + increase_step
211
+ end
212
+
213
+ def increase_price_to(overcome:, ceil:)
214
+ return ceil if overcome == ceil
215
+ raise ":ceil is lower than :overcome" if ceil < overcome
216
+
217
+ running_price = current_price
218
+ while running_price <= overcome
219
+ running_price = increase_price(running_price)
220
+ end
221
+
222
+ [running_price, ceil].min
223
+ end
224
+
225
+ def bid_steps_ladder
226
+ @bid_steps_ladder ||= auction.bid_steps_ladder
227
+ end
228
+
229
+ def changing_own_limit?
230
+ bid.with_limit? && winning_bid.present? && (winning_bid.bidder == bid.bidder)
231
+ end
232
+
233
+ def overbidding_yourself_allowed?
234
+ Auctify.configuration.restrict_overbidding_yourself_to_max_price_increasing == false
235
+ end
236
+
237
+ def same_bidder?(winning_bid, new_bid)
238
+ winning_bid.bidder == new_bid.bidder
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Base (surprise!) for Auctify services.
4
+ # Ascendants shoudl implement own methods `initialize` and `build_result`(which assigns to `@result`).
5
+ # In succesfull flow, call `srv = SomeService.call(*args)`
6
+ # will return itself with result accessible by `srv.result`, errors by`srv.errors` and `srv.success? => true`
7
+ # In failed call (calling `fail!` when building result),
8
+ # there can be still some result, even errors and `srv.success? => false` (or `srv.failed? => true`)
9
+ # Failing and errors are independent!
10
+ # Errors are like ActiveRecord errors, use `errors.add(:key, "message)`
11
+ # You can pass some information through `srv.flashes` hash
12
+
13
+ module Auctify
14
+ class ServiceErrors < Hash
15
+ def add(key, value, _opts = {})
16
+ self[key] ||= []
17
+ self[key] << value
18
+ self[key].uniq!
19
+ end
20
+
21
+ def add_from_hash(errors_hash)
22
+ errors_hash.each do |key, values|
23
+ values.each { |value| add key, value }
24
+ end
25
+ end
26
+
27
+ def full_messages
28
+ f_msgs = []
29
+ each_one { |field, message| f_msgs << "#{field}: #{message}" }
30
+ f_msgs
31
+ end
32
+
33
+ def each_one
34
+ each_pair do |field, messages|
35
+ messages.each { |message| yield field, message }
36
+ end
37
+ end
38
+ end
39
+
40
+ class ServiceBase
41
+ attr_reader :result, :errors, :flashes
42
+
43
+ def self.call(*args, **keyword_args)
44
+ if args.blank?
45
+ new(**keyword_args).call
46
+ elsif keyword_args.blank?
47
+ new(*args).call
48
+ else
49
+ new(*args, **keyword_args).call
50
+ end
51
+ end
52
+
53
+ def call
54
+ build_result
55
+ self # always returnning service itself, to get to `errors`, `result`
56
+ end
57
+
58
+ def initialize
59
+ @result = nil
60
+ @failed = false
61
+ @flashes = {}
62
+ @errors = ServiceErrors.new
63
+ end
64
+
65
+ def fail!
66
+ @failed = true
67
+ end
68
+
69
+ def success?
70
+ !failure?
71
+ end
72
+
73
+ def failure?
74
+ @failed
75
+ end
76
+ alias failed? failure?
77
+ end
78
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auctify
4
+ # in ruby/rails we expecting default format as hash with ranges { (0...1_000) => 200, (1_000..) => 500 }
5
+ # but acceppting (in setter) also hash with numbers or strings
6
+ # in DB we store JSON with minimal prices casted to nice strings : { "0" => 200, "1_000" => 500 }
7
+ class MinimalBidsLadderType < ActiveRecord::Type::Value
8
+ def type
9
+ :jsonb
10
+ end
11
+
12
+ def cast(value) # setter
13
+ # hash is expected as value
14
+ value = JSON.parse(value) if value.is_a?(String)
15
+ if value.blank?
16
+ {}
17
+ else
18
+ case value.keys.first
19
+ when Range # { (0...1_000) => 200, (1_000..) => 500 }
20
+ cast_from_ranges(value)
21
+ when String # { "0" => 200, "1_000" => 500 }
22
+ cast_from_min_strings(value)
23
+ when Numeric # { 0 => 200, 1000 => 500 }
24
+ cast_from_min_numbers(value)
25
+ else
26
+ raise "Uncovered ladder key type `#{value.keys.first}`"
27
+ end
28
+ end
29
+ end
30
+
31
+ def deserialize(db_value) # getter, from db data to ruby raw object
32
+ return {} if db_value.blank?
33
+
34
+ cast(ActiveSupport::JSON.decode(db_value))
35
+ end
36
+
37
+ def serialize(value) # modifier to store in db
38
+ return nil if value.nil? # so You are able to do `KLASS.where(bid_steps_ladder: nil)`
39
+
40
+ # value should be Hash with ranges as keys
41
+ result = {}
42
+
43
+ value.each_pair do |k_range, v|
44
+ min_price_string = ActiveSupport::NumberHelper.number_to_delimited(k_range.first, delimiter: "_")
45
+ result[min_price_string] = v
46
+ end
47
+
48
+ ActiveSupport::JSON.encode(result)
49
+ end
50
+
51
+ private
52
+ def cast_from_ranges(hash)
53
+ hash # desired format
54
+ end
55
+
56
+ def cast_from_min_strings(hash)
57
+ result = {}
58
+ hash.each_pair { |k, v| result[k.to_i] = v }
59
+
60
+ cast_from_min_numbers(result)
61
+ end
62
+
63
+ def cast_from_min_numbers(hash)
64
+ previous_step = nil
65
+
66
+ result = hash.to_a.sort.each_with_object({}) do |step, rslt|
67
+ unless previous_step.nil?
68
+ rslt[Range.new(previous_step.first, step.first, true)] = previous_step.last
69
+ end
70
+ previous_step = step
71
+ end
72
+ result[Range.new(previous_step.first, nil, false)] = previous_step.last
73
+
74
+ result
75
+ end
76
+ end
77
+ end
@@ -0,0 +1 @@
1
+ <p>Bids.count: <%= @bids.count %> </p>
@@ -0,0 +1 @@
1
+ <p>BID: <%= @bid.to_json %></p>
@@ -0,0 +1,27 @@
1
+ <%= form_with(model: bidder_registration, local: true) do |form| %>
2
+ <% if bidder_registration.errors.any? %>
3
+ <div id="error_explanation">
4
+ <h2><%= pluralize(bidder_registration.errors.count, "error") %> prohibited this bidder_registration from being saved:</h2>
5
+
6
+ <ul>
7
+ <% bidder_registration.errors.full_messages.each do |message| %>
8
+ <li><%= message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div class="field">
15
+ <%= form.label :bidder_id %>
16
+ <%= form.text_field :bidder_id %>
17
+ </div>
18
+
19
+ <div class="field">
20
+ <%= form.label :auction_id %>
21
+ <%= form.text_field :auction_id %>
22
+ </div>
23
+
24
+ <div class="actions">
25
+ <%= form.submit %>
26
+ </div>
27
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <h1>Editing Bidder Registration</h1>
2
+
3
+ <%= render 'form', bidder_registration: @bidder_registration %>
4
+
5
+ <%= link_to 'Show', @bidder_registration %> |
6
+ <%= link_to 'Back', auctify_bidder_registrations_path %>
@@ -0,0 +1,33 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <h1>Bidder Registrations</h1>
4
+
5
+ <table>
6
+ <thead>
7
+ <tr>
8
+ <th>Bidder</th>
9
+ <th>Auction</th>
10
+ <th>Aasm state</th>
11
+ <th>Handled at</th>
12
+ <th colspan="3"></th>
13
+ </tr>
14
+ </thead>
15
+
16
+ <tbody>
17
+ <% @bidder_registrations.each do |bidder_registration| %>
18
+ <tr>
19
+ <td><%= bidder_registration.bidder_id %></td>
20
+ <td><%= bidder_registration.auction_id %></td>
21
+ <td><%= bidder_registration.aasm_state %></td>
22
+ <td><%= bidder_registration.handled_at %></td>
23
+ <td><%= link_to 'Show', bidder_registration %></td>
24
+ <td><%= link_to 'Edit', edit_auctify_bidder_registration_path(bidder_registration) %></td>
25
+ <td><%= link_to 'Destroy', bidder_registration, method: :delete, data: { confirm: 'Are you sure?' } %></td>
26
+ </tr>
27
+ <% end %>
28
+ </tbody>
29
+ </table>
30
+
31
+ <br>
32
+
33
+ <%= link_to 'New Bidder Registration', new_auctify_bidder_registration_path %>