auctify 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/app/controllers/auctify/api/v1/auctions_controller.rb +17 -4
- data/app/controllers/auctify/bidder_registrations_controller.rb +5 -2
- data/app/jobs/auctify/autobid_filler_job.rb +14 -0
- data/app/jobs/auctify/bidding_closer_job.rb +5 -5
- data/app/jobs/auctify/bidding_is_close_to_end_notifier_job.rb +1 -1
- data/app/jobs/auctify/ensure_auctions_closing_job.rb +24 -0
- data/app/models/auctify/bid.rb +26 -1
- data/app/models/auctify/bidder_registration.rb +19 -0
- data/app/models/auctify/sale/auction.rb +74 -45
- data/app/models/auctify/sale/base.rb +40 -35
- data/app/models/auctify/sale/retail.rb +38 -31
- data/app/models/auctify/sales_pack.rb +51 -16
- data/app/services/auctify/bids_appender.rb +4 -2
- data/config/locales/auctify.cs.yml +22 -4
- data/db/migrate/20210825155543_change_commission_columns.rb +8 -0
- data/db/migrate/20210917082313_add_auction_prolonging_limit_to_sales_packs.rb +7 -0
- data/db/migrate/20211022133253_add_autobid_to_auctify_bids.rb +12 -0
- data/db/migrate/20211203064934_update_sales_featurable.rb +25 -0
- data/lib/auctify/configuration.rb +7 -3
- data/lib/auctify/engine.rb +1 -0
- data/lib/auctify/version.rb +1 -1
- metadata +32 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13a52b4af50580a4b82abdb1c7fd04686803f3a59d648b2a0fb18bc7acb46875
|
4
|
+
data.tar.gz: c33891089eb4f6e53d101b033442dc56dd62966a2b0aef5d1173e73c0070335d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33f938020a0879f29ce53ac4f0a926138f9c904cdcb435ecdf4ac79119300e450394861c12f611e6248ac5136ab7e94516a3232d9451bf339a682509ce2eac92
|
7
|
+
data.tar.gz: a68e770af42e0c5be75ff52e47bccc428d80be1c49b5744623cd26d0c0aa5b01d6700d5e385f1e0ad3e41448aeaf7ff66efbb8b5fd0414b3c562e35041b64776
|
data/README.md
CHANGED
@@ -65,7 +65,7 @@ If `item.owner` exists, it is used as `auction.seller` (or Select from all aucit
|
|
65
65
|
- item can have category and user can select auction by categories.
|
66
66
|
- SalePack can be `(un)published /public`
|
67
67
|
- SalePack can be `open (adding items , bidding)/ closed(bidding ended)`
|
68
|
-
-
|
68
|
+
- auctioneer_commision_from_buyer is in % and adds to sold_price in checkout
|
69
69
|
- auction have `start_time` and `end_time`
|
70
70
|
- auction can be `highlighted` for cover pages
|
71
71
|
- auction stores all bids history even those cancelled by admin
|
@@ -125,7 +125,7 @@ end
|
|
125
125
|
```ruby
|
126
126
|
Auctify.configure do |config|
|
127
127
|
config.autoregister_as_bidders_all_instances_of_classes = ["User"] # default is []
|
128
|
-
config.
|
128
|
+
config.auction_prolonging_limit_in_seconds = 10.minutes # default is 1.minute, can be overriden in `SalePack#auction_prolonging_limit_in_seconds` attribute
|
129
129
|
config.auctioneer_commission_in_percent = 10 # so buyer will pay: auction.current_price * ((100 + 10)/100)
|
130
130
|
config.autofinish_auction_after_bidding = true # after `auction.close_bidding!` immediatelly proces result to `auction.sold_in_auction!` or `auction.not_sold_in_auction!`; default false
|
131
131
|
config.when_to_notify_bidders_before_end_of_bidding = 30.minutes # default `nil` => no notifying
|
@@ -7,7 +7,11 @@ module Auctify
|
|
7
7
|
before_action :find_auction, except: [:index]
|
8
8
|
|
9
9
|
def show
|
10
|
-
|
10
|
+
if params[:updated_at].present? && params[:updated_at].match?(/\A\d+\z/) && params[:updated_at].to_i == @auction.updated_at.to_i
|
11
|
+
render json: { current: true }, status: 200
|
12
|
+
else
|
13
|
+
render_record @auction
|
14
|
+
end
|
11
15
|
end
|
12
16
|
|
13
17
|
def bids
|
@@ -20,7 +24,12 @@ module Auctify
|
|
20
24
|
.update_all(dont_confirm_bids: true)
|
21
25
|
end
|
22
26
|
|
23
|
-
|
27
|
+
@auction.reload
|
28
|
+
|
29
|
+
winning_bid_id = @auction.winning_bid.try(:id)
|
30
|
+
overbid_by_limit = winning_bid_id && new_bid.id && winning_bid_id != new_bid.id
|
31
|
+
|
32
|
+
render_record @auction, success: true, overbid_by_limit: overbid_by_limit
|
24
33
|
else
|
25
34
|
render_record @auction, bid: new_bid, status: 400
|
26
35
|
end
|
@@ -50,9 +59,13 @@ module Auctify
|
|
50
59
|
{ bidder: current_user }
|
51
60
|
end
|
52
61
|
|
53
|
-
def render_record(auction, bid: nil, status: 200, success: nil)
|
62
|
+
def render_record(auction, bid: nil, status: 200, success: nil, overbid_by_limit: nil)
|
54
63
|
render json: {
|
55
|
-
data: cell("#{global_namespace_path}/auctify/auctions/form",
|
64
|
+
data: cell("#{global_namespace_path}/auctify/auctions/form",
|
65
|
+
auction,
|
66
|
+
bid: bid,
|
67
|
+
success: success,
|
68
|
+
overbid_by_limit: overbid_by_limit).show
|
56
69
|
}, status: status
|
57
70
|
end
|
58
71
|
end
|
@@ -46,8 +46,11 @@ module Auctify
|
|
46
46
|
|
47
47
|
# DELETE /bidder_registrations/1
|
48
48
|
def destroy
|
49
|
-
@bidder_registration.destroy
|
50
|
-
|
49
|
+
if @bidder_registration.destroy
|
50
|
+
redirect_to auctify_bidder_registrations_url, notice: "Bidder registration was successfully destroyed."
|
51
|
+
else
|
52
|
+
render json: { errors: @bidder_registration.errors }
|
53
|
+
end
|
51
54
|
end
|
52
55
|
|
53
56
|
private
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Auctify
|
4
|
+
class AutobidFillerJob < Auctify::ApplicationJob
|
5
|
+
queue_as :default
|
6
|
+
|
7
|
+
def perform
|
8
|
+
reg_ids = Auctify::Bid.pluck(:registration_id).uniq
|
9
|
+
registrations_with_bids = Auctify::BidderRegistration.where(id: reg_ids)
|
10
|
+
|
11
|
+
registrations_with_bids.find_each { |reg| reg.fillup_autobid_flags! }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -12,11 +12,11 @@ module Auctify
|
|
12
12
|
return
|
13
13
|
end
|
14
14
|
|
15
|
-
if
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
return if Time.current < auction.currently_ends_at
|
16
|
+
|
17
|
+
Auctify::Sale::Auction.with_advisory_lock("closing_auction_#{auction_id}") do
|
18
|
+
# can wait unitl other BCJob release lock and than continue!
|
19
|
+
auction.close_bidding! if auction.reload.in_sale?
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Auctify
|
4
|
+
class EnsureAuctionsClosingJob < ApplicationJob
|
5
|
+
queue_as :critical
|
6
|
+
|
7
|
+
def perform
|
8
|
+
auctions = ::Auctify::Sale::Auction.in_sale
|
9
|
+
.where("currently_ends_at <= ?", Time.current + checking_period_to_future)
|
10
|
+
auctions.each do |auction|
|
11
|
+
if auction.currently_ends_at <= Time.current
|
12
|
+
Auctify::BiddingCloserJob.perform_now(auction_id: auction.id)
|
13
|
+
else
|
14
|
+
Auctify::BiddingCloserJob.set(wait_until: auction.currently_ends_at).perform_later(auction_id: auction.id)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def checking_period_to_future
|
21
|
+
Auctify.configuration.auction_prolonging_limit_in_seconds || 5.minutes
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/app/models/auctify/bid.rb
CHANGED
@@ -10,6 +10,14 @@ module Auctify
|
|
10
10
|
scope :with_limit, -> { where.not(max_price: nil) }
|
11
11
|
|
12
12
|
validate :price_is_not_bigger_then_max_price
|
13
|
+
validate :price_is_rounded
|
14
|
+
|
15
|
+
def <=>(other)
|
16
|
+
r = (self.price <=> other.price)
|
17
|
+
r = (self.created_at <=> other.created_at) if r.zero?
|
18
|
+
r = (self.id <=> other.id) if r.zero?
|
19
|
+
r
|
20
|
+
end
|
13
21
|
|
14
22
|
def cancel!
|
15
23
|
update!(cancelled: true)
|
@@ -18,7 +26,11 @@ module Auctify
|
|
18
26
|
end
|
19
27
|
|
20
28
|
def with_limit?
|
21
|
-
|
29
|
+
limit.present?
|
30
|
+
end
|
31
|
+
|
32
|
+
def limit
|
33
|
+
max_price
|
22
34
|
end
|
23
35
|
|
24
36
|
def bade_at
|
@@ -29,6 +41,12 @@ module Auctify
|
|
29
41
|
errors.add(:price, :must_be_lower_or_equal_max_price) if max_price && max_price < price
|
30
42
|
end
|
31
43
|
|
44
|
+
def price_is_rounded
|
45
|
+
round_to = configuration.require_bids_to_be_rounded_to
|
46
|
+
errors.add(:price, :must_be_rounded_to, { round_to: round_to }) if price && (price != round_it_to(price, round_to))
|
47
|
+
errors.add(:max_price, :must_be_rounded_to, { round_to: round_to }) if max_price && (max_price != round_it_to(max_price, round_to))
|
48
|
+
end
|
49
|
+
|
32
50
|
def bidder=(auctified_model)
|
33
51
|
errors.add(:bidder, :not_auctified) unless auctified_model.class.included_modules.include?(Auctify::Behavior::Buyer)
|
34
52
|
raise "There is already registration for this bid!" if registration.present?
|
@@ -46,6 +64,12 @@ module Auctify
|
|
46
64
|
def configuration
|
47
65
|
Auctify.configuration
|
48
66
|
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def round_it_to(amount, smallest_amount)
|
70
|
+
smallest_amount = smallest_amount.to_i
|
71
|
+
(smallest_amount * ((amount + (smallest_amount / 2)).to_i / smallest_amount))
|
72
|
+
end
|
49
73
|
end
|
50
74
|
end
|
51
75
|
|
@@ -60,6 +84,7 @@ end
|
|
60
84
|
# created_at :datetime not null
|
61
85
|
# updated_at :datetime not null
|
62
86
|
# cancelled :boolean default(FALSE)
|
87
|
+
# autobid :boolean default(FALSE)
|
63
88
|
#
|
64
89
|
# Indexes
|
65
90
|
#
|
@@ -49,6 +49,25 @@ module Auctify
|
|
49
49
|
|
50
50
|
validate :auction_is_in_allowed_state, on: :create
|
51
51
|
|
52
|
+
def fillup_autobid_flags!
|
53
|
+
current_limit = 0
|
54
|
+
ordered_applied_bids.reverse_each do |bid|
|
55
|
+
if bid.with_limit?
|
56
|
+
if current_limit < bid.limit
|
57
|
+
# increase of limit
|
58
|
+
bid.autobid = false
|
59
|
+
current_limit = bid.limit
|
60
|
+
else
|
61
|
+
# same limit, same registration, younger bid => autobid
|
62
|
+
bid.autobid = true
|
63
|
+
end
|
64
|
+
else
|
65
|
+
bid.autobid = false
|
66
|
+
end
|
67
|
+
bid.save!(validate: false)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
52
71
|
private
|
53
72
|
def auction_is_in_allowed_state
|
54
73
|
unless auction && auction.allows_new_bidder_registrations?
|
@@ -60,8 +60,6 @@ module Auctify
|
|
60
60
|
transitions from: :accepted, to: :in_sale
|
61
61
|
|
62
62
|
after do
|
63
|
-
set_bidding_closer_job
|
64
|
-
set_bidding_is_close_to_end_job
|
65
63
|
after_start_sale
|
66
64
|
end
|
67
65
|
end
|
@@ -119,6 +117,7 @@ module Auctify
|
|
119
117
|
validate :forbidden_changes
|
120
118
|
|
121
119
|
after_create :autoregister_bidders
|
120
|
+
after_save :create_jobs
|
122
121
|
before_destroy :forbid_destroy_if_there_are_bids, prepend: true
|
123
122
|
|
124
123
|
def bidders
|
@@ -217,9 +216,11 @@ module Auctify
|
|
217
216
|
bidding_result.current_minimal_bid
|
218
217
|
end
|
219
218
|
|
220
|
-
def current_max_price_for(bidder)
|
221
|
-
|
222
|
-
|
219
|
+
def current_max_price_for(bidder, bids_array: nil)
|
220
|
+
bids_array ||= ordered_applied_bids.with_limit
|
221
|
+
|
222
|
+
last_bidder_mx_bid = bids_array.detect { |bid| !bid.max_price.nil? && bid.bidder == bidder }
|
223
|
+
|
223
224
|
last_bidder_mx_bid.blank? ? 0 : last_bidder_mx_bid.max_price
|
224
225
|
end
|
225
226
|
|
@@ -244,9 +245,8 @@ module Auctify
|
|
244
245
|
applied_bids_count.positive?
|
245
246
|
end
|
246
247
|
|
247
|
-
def
|
248
|
-
Auctify
|
249
|
-
.perform_later(auction_id: id)
|
248
|
+
def auction_prolonging_limit_in_seconds
|
249
|
+
pack&.auction_prolonging_limit_in_seconds || Auctify.configuration.auction_prolonging_limit_in_seconds
|
250
250
|
end
|
251
251
|
|
252
252
|
private
|
@@ -311,7 +311,7 @@ module Auctify
|
|
311
311
|
end
|
312
312
|
|
313
313
|
def extend_end_time(bid_time)
|
314
|
-
new_end_time = bid_time +
|
314
|
+
new_end_time = bid_time + auction_prolonging_limit_in_seconds
|
315
315
|
self.currently_ends_at = [currently_ends_at, new_end_time].max
|
316
316
|
end
|
317
317
|
|
@@ -326,16 +326,17 @@ module Auctify
|
|
326
326
|
end
|
327
327
|
end
|
328
328
|
|
329
|
-
def
|
329
|
+
def bidding_is_close_to_end_notification_time
|
330
330
|
configured_period = Auctify.configuration.when_to_notify_bidders_before_end_of_bidding
|
331
|
-
return if configured_period.blank?
|
332
|
-
|
333
|
-
|
334
|
-
.perform_later(auction_id: id)
|
331
|
+
return nil if configured_period.blank?
|
332
|
+
|
333
|
+
ends_at - configured_period
|
335
334
|
end
|
336
335
|
|
337
336
|
def forbidden_changes
|
338
337
|
ATTRIBUTES_UNMUTABLE_AT_SOME_STATE.each do |att|
|
338
|
+
next if configuration.allow_changes_on_auction_with_bids_for_attributes.include?(att.to_sym)
|
339
|
+
|
339
340
|
if changes[att].present? && locked_for_modifications?
|
340
341
|
errors.add(att, :no_modification_allowed_now)
|
341
342
|
write_attribute(att, changes[att].first)
|
@@ -349,6 +350,20 @@ module Auctify
|
|
349
350
|
def forbid_destroy_if_there_are_bids
|
350
351
|
errors.add(:base, :you_cannot_delete_auction_with_bids) if bids.any?
|
351
352
|
end
|
353
|
+
|
354
|
+
def create_jobs(force = false)
|
355
|
+
return unless in_sale?
|
356
|
+
|
357
|
+
currently_ends_at_changes = saved_changes["currently_ends_at"]
|
358
|
+
|
359
|
+
if force || currently_ends_at_changes.present?
|
360
|
+
if (notify_time = bidding_is_close_to_end_notification_time).present?
|
361
|
+
# remove_old job is unsupported in ActiveJob
|
362
|
+
Auctify::BiddingIsCloseToEndNotifierJob.set(wait_until: notify_time)
|
363
|
+
.perform_later(auction_id: id)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
352
367
|
end
|
353
368
|
end
|
354
369
|
end
|
@@ -357,37 +372,51 @@ end
|
|
357
372
|
#
|
358
373
|
# Table name: auctify_sales
|
359
374
|
#
|
360
|
-
# id
|
361
|
-
# seller_type
|
362
|
-
# seller_id
|
363
|
-
# buyer_type
|
364
|
-
# buyer_id
|
365
|
-
# item_id
|
366
|
-
# created_at
|
367
|
-
# updated_at
|
368
|
-
# type
|
369
|
-
# aasm_state
|
370
|
-
# offered_price
|
371
|
-
# current_price
|
372
|
-
# sold_price
|
373
|
-
# bid_steps_ladder
|
374
|
-
# reserve_price
|
375
|
-
# pack_id
|
376
|
-
# ends_at
|
377
|
-
# position
|
378
|
-
# number
|
379
|
-
# currently_ends_at
|
380
|
-
# published
|
381
|
-
#
|
382
|
-
#
|
383
|
-
#
|
384
|
-
#
|
385
|
-
#
|
386
|
-
#
|
387
|
-
#
|
388
|
-
#
|
389
|
-
#
|
390
|
-
#
|
375
|
+
# id :bigint(8) not null, primary key
|
376
|
+
# seller_type :string
|
377
|
+
# seller_id :integer
|
378
|
+
# buyer_type :string
|
379
|
+
# buyer_id :integer
|
380
|
+
# item_id :integer not null
|
381
|
+
# created_at :datetime not null
|
382
|
+
# updated_at :datetime not null
|
383
|
+
# type :string default("Auctify::Sale::Base")
|
384
|
+
# aasm_state :string default("offered"), not null
|
385
|
+
# offered_price :decimal(, )
|
386
|
+
# current_price :decimal(, )
|
387
|
+
# sold_price :decimal(, )
|
388
|
+
# bid_steps_ladder :json
|
389
|
+
# reserve_price :decimal(, )
|
390
|
+
# pack_id :bigint(8)
|
391
|
+
# ends_at :datetime
|
392
|
+
# position :integer
|
393
|
+
# number :string
|
394
|
+
# currently_ends_at :datetime
|
395
|
+
# published :boolean default(FALSE)
|
396
|
+
# slug :string
|
397
|
+
# contract_number :string
|
398
|
+
# seller_commission_in_percent :integer
|
399
|
+
# winner_type :string
|
400
|
+
# winner_id :bigint(8)
|
401
|
+
# applied_bids_count :integer default(0)
|
402
|
+
# sold_at :datetime
|
403
|
+
# current_winner_type :string
|
404
|
+
# current_winner_id :bigint(8)
|
405
|
+
# buyer_commission_in_percent :integer
|
406
|
+
# featured :integer
|
407
|
+
#
|
408
|
+
# Indexes
|
409
|
+
#
|
410
|
+
# index_auctify_sales_on_buyer_type_and_buyer_id (buyer_type,buyer_id)
|
411
|
+
# index_auctify_sales_on_currently_ends_at (currently_ends_at)
|
412
|
+
# index_auctify_sales_on_featured (featured)
|
413
|
+
# index_auctify_sales_on_pack_id (pack_id)
|
414
|
+
# index_auctify_sales_on_position (position)
|
415
|
+
# index_auctify_sales_on_published (published)
|
416
|
+
# index_auctify_sales_on_seller_type_and_seller_id (seller_type,seller_id)
|
417
|
+
# index_auctify_sales_on_slug (slug) UNIQUE
|
418
|
+
# index_auctify_sales_on_winner_type_and_winner_id (winner_type,winner_id)
|
419
|
+
#
|
391
420
|
|
392
421
|
#
|
393
422
|
# Indexes
|
@@ -4,8 +4,7 @@ module Auctify
|
|
4
4
|
module Sale
|
5
5
|
class Base < ApplicationRecord
|
6
6
|
include Folio::FriendlyId
|
7
|
-
include Folio::
|
8
|
-
include Folio::Featurable::Basic
|
7
|
+
include Folio::Featurable::WithPosition
|
9
8
|
include Folio::Publishable::Basic
|
10
9
|
|
11
10
|
self.table_name = "auctify_sales"
|
@@ -33,6 +32,7 @@ module Auctify
|
|
33
32
|
validate :validate_offered_price_when_published
|
34
33
|
|
35
34
|
scope :not_sold, -> { where(sold_price: nil) }
|
35
|
+
scope :ordered, -> { order(currently_ends_at: :asc, id: :asc) }
|
36
36
|
|
37
37
|
# need auction scopes here because of has_many :sales, class_name: "Auctify::Sale::Base"
|
38
38
|
scope :auctions_open_for_bids, -> do
|
@@ -92,10 +92,14 @@ module Auctify
|
|
92
92
|
save
|
93
93
|
end
|
94
94
|
|
95
|
-
def
|
95
|
+
def auctioneer_commision_from_seller
|
96
|
+
(seller_commission_in_percent || 0) * 0.01 * offered_price
|
97
|
+
end
|
98
|
+
|
99
|
+
def auctioneer_commision_from_buyer
|
96
100
|
return nil if sold_price.nil?
|
97
101
|
|
98
|
-
percent =
|
102
|
+
percent = buyer_commission_in_percent \
|
99
103
|
|| (pack&.commission_in_percent) \
|
100
104
|
|| Auctify.configuration.auctioneer_commission_in_percent
|
101
105
|
|
@@ -164,37 +168,38 @@ end
|
|
164
168
|
#
|
165
169
|
# Table name: auctify_sales
|
166
170
|
#
|
167
|
-
# id
|
168
|
-
# seller_type
|
169
|
-
# seller_id
|
170
|
-
# buyer_type
|
171
|
-
# buyer_id
|
172
|
-
# item_id
|
173
|
-
# created_at
|
174
|
-
# updated_at
|
175
|
-
# type
|
176
|
-
# aasm_state
|
177
|
-
# offered_price
|
178
|
-
# current_price
|
179
|
-
# sold_price
|
180
|
-
# bid_steps_ladder
|
181
|
-
# reserve_price
|
182
|
-
# pack_id
|
183
|
-
# ends_at
|
184
|
-
# position
|
185
|
-
# number
|
186
|
-
# currently_ends_at
|
187
|
-
# published
|
188
|
-
#
|
189
|
-
#
|
190
|
-
#
|
191
|
-
#
|
192
|
-
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
171
|
+
# id :bigint(8) not null, primary key
|
172
|
+
# seller_type :string
|
173
|
+
# seller_id :integer
|
174
|
+
# buyer_type :string
|
175
|
+
# buyer_id :integer
|
176
|
+
# item_id :integer not null
|
177
|
+
# created_at :datetime not null
|
178
|
+
# updated_at :datetime not null
|
179
|
+
# type :string default("Auctify::Sale::Base")
|
180
|
+
# aasm_state :string default("offered"), not null
|
181
|
+
# offered_price :decimal(, )
|
182
|
+
# current_price :decimal(, )
|
183
|
+
# sold_price :decimal(, )
|
184
|
+
# bid_steps_ladder :json
|
185
|
+
# reserve_price :decimal(, )
|
186
|
+
# pack_id :bigint(8)
|
187
|
+
# ends_at :datetime
|
188
|
+
# position :integer
|
189
|
+
# number :string
|
190
|
+
# currently_ends_at :datetime
|
191
|
+
# published :boolean default(FALSE)
|
192
|
+
# slug :string
|
193
|
+
# contract_number :string
|
194
|
+
# seller_commission_in_percent :integer
|
195
|
+
# winner_type :string
|
196
|
+
# winner_id :bigint(8)
|
197
|
+
# applied_bids_count :integer default(0)
|
198
|
+
# sold_at :datetime
|
199
|
+
# current_winner_type :string
|
200
|
+
# current_winner_id :bigint(8)
|
201
|
+
# buyer_commission_in_percent :integer
|
202
|
+
# featured :integer
|
198
203
|
#
|
199
204
|
# Indexes
|
200
205
|
#
|
@@ -23,6 +23,12 @@ module Auctify
|
|
23
23
|
end
|
24
24
|
|
25
25
|
event :start_sale do
|
26
|
+
before do
|
27
|
+
self.current_price = self.offered_price
|
28
|
+
self.currently_ends_at = self.ends_at
|
29
|
+
self.buyer = nil
|
30
|
+
end
|
31
|
+
|
26
32
|
transitions from: :accepted, to: :in_sale
|
27
33
|
end
|
28
34
|
|
@@ -51,37 +57,38 @@ end
|
|
51
57
|
#
|
52
58
|
# Table name: auctify_sales
|
53
59
|
#
|
54
|
-
# id
|
55
|
-
# seller_type
|
56
|
-
# seller_id
|
57
|
-
# buyer_type
|
58
|
-
# buyer_id
|
59
|
-
# item_id
|
60
|
-
# created_at
|
61
|
-
# updated_at
|
62
|
-
# type
|
63
|
-
# aasm_state
|
64
|
-
# offered_price
|
65
|
-
# current_price
|
66
|
-
# sold_price
|
67
|
-
# bid_steps_ladder
|
68
|
-
# reserve_price
|
69
|
-
# pack_id
|
70
|
-
# ends_at
|
71
|
-
# position
|
72
|
-
# number
|
73
|
-
# currently_ends_at
|
74
|
-
# published
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
60
|
+
# id :bigint(8) not null, primary key
|
61
|
+
# seller_type :string
|
62
|
+
# seller_id :integer
|
63
|
+
# buyer_type :string
|
64
|
+
# buyer_id :integer
|
65
|
+
# item_id :integer not null
|
66
|
+
# created_at :datetime not null
|
67
|
+
# updated_at :datetime not null
|
68
|
+
# type :string default("Auctify::Sale::Base")
|
69
|
+
# aasm_state :string default("offered"), not null
|
70
|
+
# offered_price :decimal(, )
|
71
|
+
# current_price :decimal(, )
|
72
|
+
# sold_price :decimal(, )
|
73
|
+
# bid_steps_ladder :json
|
74
|
+
# reserve_price :decimal(, )
|
75
|
+
# pack_id :bigint(8)
|
76
|
+
# ends_at :datetime
|
77
|
+
# position :integer
|
78
|
+
# number :string
|
79
|
+
# currently_ends_at :datetime
|
80
|
+
# published :boolean default(FALSE)
|
81
|
+
# slug :string
|
82
|
+
# contract_number :string
|
83
|
+
# seller_commission_in_percent :integer
|
84
|
+
# winner_type :string
|
85
|
+
# winner_id :bigint(8)
|
86
|
+
# applied_bids_count :integer default(0)
|
87
|
+
# sold_at :datetime
|
88
|
+
# current_winner_type :string
|
89
|
+
# current_winner_id :bigint(8)
|
90
|
+
# buyer_commission_in_percent :integer
|
91
|
+
# featured :integer
|
85
92
|
#
|
86
93
|
# Indexes
|
87
94
|
#
|
@@ -29,6 +29,7 @@ module Auctify
|
|
29
29
|
numericality: { greater_than_or_equal: 0, less_than: 60 }
|
30
30
|
|
31
31
|
validate :validate_start_and_end_dates
|
32
|
+
validate :sales_ends_in_pack_time_frame
|
32
33
|
|
33
34
|
scope :ordered, -> { order(start_date: :desc, id: :desc) }
|
34
35
|
|
@@ -57,6 +58,22 @@ module Auctify
|
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
61
|
+
def shift_sales_by_minutes!(shift_in_minutes)
|
62
|
+
self.transaction do
|
63
|
+
sales.each do |sale|
|
64
|
+
sale.update!(ends_at: sale.ends_at + shift_in_minutes.minutes)
|
65
|
+
|
66
|
+
validate_sale_ends_in_time_frame(sale)
|
67
|
+
raise ActiveRecord::RecordInvalid if errors[:sales].present?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
sales.reload
|
71
|
+
end
|
72
|
+
|
73
|
+
def time_frame
|
74
|
+
(start_date.to_time..(end_date.to_time + 1.day))
|
75
|
+
end
|
76
|
+
|
60
77
|
private
|
61
78
|
def validate_start_and_end_dates
|
62
79
|
if start_date.present? && end_date.present? && start_date > end_date
|
@@ -64,11 +81,28 @@ module Auctify
|
|
64
81
|
end
|
65
82
|
end
|
66
83
|
|
84
|
+
def sales_ends_in_pack_time_frame
|
85
|
+
return if changes["start_date"].present? || changes["end_date"]
|
86
|
+
|
87
|
+
sales.select(:id, :slug, :ends_at).each do |sale|
|
88
|
+
validate_sale_ends_in_time_frame(sale)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
67
92
|
def set_commission
|
68
93
|
return if self.commission_in_percent.present?
|
69
94
|
|
70
95
|
self.commission_in_percent = Auctify.configuration.auctioneer_commission_in_percent
|
71
96
|
end
|
97
|
+
|
98
|
+
def validate_sale_ends_in_time_frame(sale)
|
99
|
+
unless time_frame.cover?(sale.ends_at)
|
100
|
+
errors.add(:sales,
|
101
|
+
:sale_is_out_of_time_frame,
|
102
|
+
slug: sale.slug.blank? ? "##{sale.id}" : sale.slug,
|
103
|
+
ends_at_time: I18n.l(sale.ends_at))
|
104
|
+
end
|
105
|
+
end
|
72
106
|
end
|
73
107
|
end
|
74
108
|
|
@@ -76,22 +110,23 @@ end
|
|
76
110
|
#
|
77
111
|
# Table name: auctify_sales_packs
|
78
112
|
#
|
79
|
-
# id
|
80
|
-
# title
|
81
|
-
# description
|
82
|
-
# position
|
83
|
-
# slug
|
84
|
-
# place
|
85
|
-
# published
|
86
|
-
# created_at
|
87
|
-
# updated_at
|
88
|
-
# sales_count
|
89
|
-
# start_date
|
90
|
-
# end_date
|
91
|
-
# sales_interval
|
92
|
-
# sales_beginning_hour
|
93
|
-
# sales_beginning_minutes
|
94
|
-
# commission_in_percent
|
113
|
+
# id :bigint(8) not null, primary key
|
114
|
+
# title :string
|
115
|
+
# description :text
|
116
|
+
# position :integer default(0)
|
117
|
+
# slug :string
|
118
|
+
# place :string
|
119
|
+
# published :boolean default(FALSE)
|
120
|
+
# created_at :datetime not null
|
121
|
+
# updated_at :datetime not null
|
122
|
+
# sales_count :integer default(0)
|
123
|
+
# start_date :date
|
124
|
+
# end_date :date
|
125
|
+
# sales_interval :integer default(3)
|
126
|
+
# sales_beginning_hour :integer default(20)
|
127
|
+
# sales_beginning_minutes :integer default(0)
|
128
|
+
# commission_in_percent :integer
|
129
|
+
# auction_prolonging_limit_in_seconds :integer
|
95
130
|
#
|
96
131
|
# Indexes
|
97
132
|
#
|
@@ -57,6 +57,7 @@ module Auctify
|
|
57
57
|
|
58
58
|
def approved_bid?
|
59
59
|
@approved_bid ||= begin
|
60
|
+
bid.valid?
|
60
61
|
check_bidder
|
61
62
|
changing_own_limit? ? check_max_price_increasing : check_price_minimum
|
62
63
|
check_same_bidder
|
@@ -114,7 +115,7 @@ module Auctify
|
|
114
115
|
end
|
115
116
|
|
116
117
|
def first_bid?
|
117
|
-
|
118
|
+
winning_bid.nil?
|
118
119
|
end
|
119
120
|
|
120
121
|
def bids
|
@@ -201,10 +202,11 @@ module Auctify
|
|
201
202
|
|
202
203
|
@updated_win_bid = winning_bid.dup
|
203
204
|
@updated_win_bid.price = [price, winning_bid.max_price].min
|
205
|
+
@updated_win_bid.autobid = true
|
204
206
|
end
|
205
207
|
|
206
208
|
def increase_price(price)
|
207
|
-
return price +
|
209
|
+
return price + Auctify.configuration.require_bids_to_be_rounded_to if bid_steps_ladder.blank?
|
208
210
|
|
209
211
|
_range, increase_step = bid_steps_ladder.detect { |range, step| range.cover?(price) }
|
210
212
|
price + increase_step
|
@@ -34,14 +34,17 @@ cs:
|
|
34
34
|
published: Zveřejněno
|
35
35
|
sales_count: Počet položek
|
36
36
|
sales: Položky
|
37
|
+
items: Předměty
|
37
38
|
sales_interval: Časový rozestup mezi předměty v minutách
|
38
39
|
sales_beginning_hour: Čas prvního předmětu (hodina)
|
39
40
|
sales_beginning_minutes: Čas prvního předmětu (minuty)
|
40
41
|
commission_in_percent: Provize aukční síně (procenta)
|
42
|
+
auction_prolonging_limit_in_seconds: Časový limit pro prodloužení aukce (sekundy)
|
41
43
|
auctify/sale/base:
|
42
44
|
buyer: Kupec
|
43
45
|
seller: Prodejce
|
44
|
-
item:
|
46
|
+
item: Předmět
|
47
|
+
pack: Prodejní balík
|
45
48
|
aasm_state: Stav
|
46
49
|
published: Zveřejněno
|
47
50
|
number: Číslo
|
@@ -58,14 +61,25 @@ cs:
|
|
58
61
|
updated_at: Změněna
|
59
62
|
reserve_price: Rezervovaná cena
|
60
63
|
ends_at: Předpokládaný konec
|
61
|
-
pack_id: Aukce
|
62
|
-
pack: Aukce
|
64
|
+
pack_id: Aukce
|
65
|
+
pack: Aukce
|
66
|
+
bidder_registrations: Registrace do aukce položky
|
67
|
+
bidders: Dražitelé
|
68
|
+
bids: Příhozy
|
69
|
+
winner: Vítěz dražby
|
70
|
+
current_winner: Aktuální výherce
|
63
71
|
auctify/bid:
|
64
72
|
price: Výše příhozu
|
65
|
-
max_price:
|
73
|
+
max_price: Limit příhozů
|
66
74
|
created_at: Přihozeno
|
67
75
|
updated_at:
|
68
76
|
registration: Registrace do aukce položky
|
77
|
+
auction: Položka aukce
|
78
|
+
bidder: Dražitel
|
79
|
+
auctify/bidder_registration:
|
80
|
+
bidder: Dražitel
|
81
|
+
auction: Položka aukce
|
82
|
+
bids: Příhozy
|
69
83
|
|
70
84
|
errors:
|
71
85
|
models:
|
@@ -98,6 +112,8 @@ cs:
|
|
98
112
|
attributes:
|
99
113
|
end_date:
|
100
114
|
smaller_than_start_date: "musí být později než začátek"
|
115
|
+
sales:
|
116
|
+
sale_is_out_of_time_frame: "Položka '%{slug}' má čas konce (%{ends_at_time}) mimo rámec aukce"
|
101
117
|
auctify/bid:
|
102
118
|
not_confirmed: "Příhoz je potřeba potvrdit"
|
103
119
|
attributes:
|
@@ -113,8 +129,10 @@ cs:
|
|
113
129
|
price_is_bellow_opening_price: "je nižší než vyvolávací cena"
|
114
130
|
price_is_bellow_minimal_bid: "je nižší než aktuální minimální příhoz %{minimal_bid}"
|
115
131
|
must_be_lower_or_equal_max_price: "musí být nižší nebo rovna maximálnímu limitu"
|
132
|
+
must_be_rounded_to: "musí být zaokrouhlená na celé %{round_to} Kč"
|
116
133
|
max_price:
|
117
134
|
price_is_bellow_minimal_bid: "je nižší než aktuální minimální příhoz %{minimal_bid}"
|
135
|
+
must_be_rounded_to: "musí být zaokrouhlená na celé %{round_to} Kč"
|
118
136
|
auctify/bidder_registration:
|
119
137
|
attributes:
|
120
138
|
auction:
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ChangeCommissionColumns < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
rename_column :auctify_sales, :commission_in_percent, :seller_commission_in_percent
|
6
|
+
add_column :auctify_sales, :buyer_commission_in_percent, :integer
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class AddAutobidToAuctifyBids < ActiveRecord::Migration[6.1]
|
4
|
+
def up
|
5
|
+
add_column :auctify_bids, :autobid, :boolean, default: false
|
6
|
+
puts("RUN `Auctify::AutobidFillerJob.perform_later`")
|
7
|
+
end
|
8
|
+
|
9
|
+
def down
|
10
|
+
remove_column :auctify_bids, :autobid
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UpdateSalesFeaturable < ActiveRecord::Migration[6.1]
|
4
|
+
def up
|
5
|
+
rename_column :auctify_sales, :featured, :featured_tmp_boolean
|
6
|
+
|
7
|
+
add_column :auctify_sales, :featured, :integer, default: nil
|
8
|
+
add_index :auctify_sales, :featured
|
9
|
+
|
10
|
+
execute("UPDATE auctify_sales SET featured = 1 WHERE featured_tmp_boolean = TRUE;")
|
11
|
+
|
12
|
+
remove_column :auctify_sales, :featured_tmp_boolean
|
13
|
+
end
|
14
|
+
|
15
|
+
def down
|
16
|
+
rename_column :auctify_sales, :featured, :featured_tmp_integer
|
17
|
+
|
18
|
+
add_column :auctify_sales, :featured, :boolean, default: false
|
19
|
+
add_index :auctify_sales, :featured
|
20
|
+
|
21
|
+
execute("UPDATE auctify_sales SET featured = TRUE WHERE featured_tmp_integer IS NOT NULL;")
|
22
|
+
|
23
|
+
remove_column :auctify_sales, :featured_tmp_integer
|
24
|
+
end
|
25
|
+
end
|
@@ -3,23 +3,27 @@
|
|
3
3
|
module Auctify
|
4
4
|
class Configuration
|
5
5
|
attr_accessor :autoregister_as_bidders_all_instances_of_classes,
|
6
|
-
:
|
6
|
+
:auction_prolonging_limit_in_seconds,
|
7
7
|
:auctioneer_commission_in_percent,
|
8
8
|
:autofinish_auction_after_bidding,
|
9
9
|
:when_to_notify_bidders_before_end_of_bidding,
|
10
10
|
:default_bid_steps_ladder,
|
11
|
-
:restrict_overbidding_yourself_to_max_price_increasing
|
11
|
+
:restrict_overbidding_yourself_to_max_price_increasing,
|
12
|
+
:require_bids_to_be_rounded_to,
|
13
|
+
:allow_changes_on_auction_with_bids_for_attributes
|
12
14
|
|
13
15
|
|
14
16
|
def initialize
|
15
17
|
# set defaults here
|
16
18
|
@autoregister_as_bidders_all_instances_of_classes = []
|
17
|
-
@
|
19
|
+
@auction_prolonging_limit_in_seconds = 2.minutes
|
18
20
|
@auctioneer_commission_in_percent = 1 # %
|
19
21
|
@autofinish_auction_after_bidding = false
|
20
22
|
@when_to_notify_bidders_before_end_of_bidding = nil # no notifying
|
21
23
|
@default_bid_steps_ladder = { 0.. => 1 }
|
22
24
|
@restrict_overbidding_yourself_to_max_price_increasing = true
|
25
|
+
@require_bids_to_be_rounded_to = 1
|
26
|
+
@allow_changes_on_auction_with_bids_for_attributes = []
|
23
27
|
end
|
24
28
|
|
25
29
|
def autoregistering_for?(instance)
|
data/lib/auctify/engine.rb
CHANGED
data/lib/auctify/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: auctify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- foton
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -16,20 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 6.
|
20
|
-
- - ">="
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: 6.0.3.7
|
19
|
+
version: 6.1.4
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
24
|
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version: 6.
|
30
|
-
- - ">="
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: 6.0.3.7
|
26
|
+
version: 6.1.4
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: aasm
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,6 +52,20 @@ dependencies:
|
|
58
52
|
- - ">="
|
59
53
|
- !ruby/object:Gem::Version
|
60
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: with_advisory_lock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
61
69
|
- !ruby/object:Gem::Dependency
|
62
70
|
name: pg
|
63
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -272,16 +280,16 @@ dependencies:
|
|
272
280
|
name: selenium-webdriver
|
273
281
|
requirement: !ruby/object:Gem::Requirement
|
274
282
|
requirements:
|
275
|
-
- - "
|
283
|
+
- - "~>"
|
276
284
|
- !ruby/object:Gem::Version
|
277
|
-
version:
|
285
|
+
version: 3.142.7
|
278
286
|
type: :development
|
279
287
|
prerelease: false
|
280
288
|
version_requirements: !ruby/object:Gem::Requirement
|
281
289
|
requirements:
|
282
|
-
- - "
|
290
|
+
- - "~>"
|
283
291
|
- !ruby/object:Gem::Version
|
284
|
-
version:
|
292
|
+
version: 3.142.7
|
285
293
|
- !ruby/object:Gem::Dependency
|
286
294
|
name: webdrivers
|
287
295
|
requirement: !ruby/object:Gem::Requirement
|
@@ -335,8 +343,10 @@ files:
|
|
335
343
|
- app/helpers/auctify/sales_helper.rb
|
336
344
|
- app/helpers/auctify/sales_packs_helper.rb
|
337
345
|
- app/jobs/auctify/application_job.rb
|
346
|
+
- app/jobs/auctify/autobid_filler_job.rb
|
338
347
|
- app/jobs/auctify/bidding_closer_job.rb
|
339
348
|
- app/jobs/auctify/bidding_is_close_to_end_notifier_job.rb
|
349
|
+
- app/jobs/auctify/ensure_auctions_closing_job.rb
|
340
350
|
- app/mailers/auctify/application_mailer.rb
|
341
351
|
- app/models/auctify/application_record.rb
|
342
352
|
- app/models/auctify/behaviors.rb
|
@@ -411,6 +421,10 @@ files:
|
|
411
421
|
- db/migrate/20210607113440_add_commission_in_percent_to_sales_packs.rb
|
412
422
|
- db/migrate/20210617062509_add_index_to_auctify_sales_currently_ends_at.rb
|
413
423
|
- db/migrate/20210625125732_add_current_winner_to_auctify_sales.rb
|
424
|
+
- db/migrate/20210825155543_change_commission_columns.rb
|
425
|
+
- db/migrate/20210917082313_add_auction_prolonging_limit_to_sales_packs.rb
|
426
|
+
- db/migrate/20211022133253_add_autobid_to_auctify_bids.rb
|
427
|
+
- db/migrate/20211203064934_update_sales_featurable.rb
|
414
428
|
- lib/auctify.rb
|
415
429
|
- lib/auctify/configuration.rb
|
416
430
|
- lib/auctify/engine.rb
|
@@ -420,7 +434,7 @@ homepage: https://github.com/sinfin/auctify
|
|
420
434
|
licenses:
|
421
435
|
- MIT
|
422
436
|
metadata: {}
|
423
|
-
post_install_message:
|
437
|
+
post_install_message:
|
424
438
|
rdoc_options: []
|
425
439
|
require_paths:
|
426
440
|
- lib
|
@@ -435,8 +449,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
435
449
|
- !ruby/object:Gem::Version
|
436
450
|
version: '0'
|
437
451
|
requirements: []
|
438
|
-
rubygems_version: 3.2.
|
439
|
-
signing_key:
|
452
|
+
rubygems_version: 3.2.28
|
453
|
+
signing_key:
|
440
454
|
specification_version: 4
|
441
455
|
summary: Gem for adding auction behavior to models.
|
442
456
|
test_files: []
|