auctify 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 %>