Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manualni zavirani aukci #46

Open
wants to merge 21 commits into
base: manual-closing
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ gem "folio", github: "sinfin/folio", branch: "master"

# To use a debugger
# gem 'byebug', group: [:development, :test]

gem "rails-i18n", "~> 6"
mreq marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 3 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,9 @@ GEM
nokogiri (>= 1.6)
rails-html-sanitizer (1.4.2)
loofah (~> 2.3)
rails-i18n (7.0.1)
rails-i18n (6.0.0)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
railties (>= 6.0.0, < 7)
railties (6.1.4.4)
actionpack (= 6.1.4.4)
activesupport (= 6.1.4.4)
Expand Down Expand Up @@ -579,6 +579,7 @@ DEPENDENCIES
pry-byebug
pry-rails
puma
rails-i18n (~> 6)
rubocop
rubocop-minitest
rubocop-performance
Expand Down
38 changes: 38 additions & 0 deletions app/controllers/auctify/api/v1/auctions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,36 @@ def bids
end
end

def close_manually
with_authorized_account do
if @auction.close_manually(by: current_account, price_check: params[:current_price])
render_record @auction.reload
else
render_record @auction.reload, status: 400
end
end
end

def lock_bidding
with_authorized_account do
if @auction.lock_bidding(by: current_account)
render_record @auction.reload
else
render_record @auction.reload, status: 400
end
end
end

def unlock_bidding
with_authorized_account do
if @auction.unlock_bidding(by: current_account)
render_record @auction.reload
else
render_record @auction.reload, status: 400
end
end
end

private
def find_auction
@auction = Auctify::Sale::Auction.find(params[:id])
Expand Down Expand Up @@ -79,6 +109,14 @@ def overbid_by_limit?(new_bid)

winning_bid != new_bid
end

def with_authorized_account(&block)
if current_account
block.call
else
head 403
end
end
end
end
end
Expand Down
6 changes: 5 additions & 1 deletion app/jobs/auctify/bidding_closer_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ def perform(auction_id:)
return
end

return if Time.current < auction.currently_ends_at
if auction.must_be_closed_manually?
return unless auction.manually_closed_at?
else
return if Time.current < auction.currently_ends_at
end

Auctify::Sale::Auction.with_advisory_lock("closing_auction_#{auction_id}") do
# can wait unitl other BCJob release lock and than continue!
Expand Down
4 changes: 3 additions & 1 deletion app/jobs/auctify/ensure_auctions_closing_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ class EnsureAuctionsClosingJob < ApplicationJob

def perform
auctions = ::Auctify::Sale::Auction.in_sale
.where("currently_ends_at <= ?", Time.current + checking_period_to_future)
.closable_automatically
.where("currently_ends_at <= ?", Time.current + checking_period_to_future)

auctions.each do |auction|
if auction.currently_ends_at <= Time.current
Auctify::BiddingCloserJob.perform_later(auction_id: auction.id)
Expand Down
2 changes: 1 addition & 1 deletion app/models/auctify/bid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def round_it_to(amount, smallest_amount)
# Table name: auctify_bids
#
# id :bigint(8) not null, primary key
# registration_id :integer not null
# registration_id :bigint(8) not null
# price :decimal(12, 2) not null
# max_price :decimal(12, 2)
# created_at :datetime not null
Expand Down
4 changes: 2 additions & 2 deletions app/models/auctify/bidder_registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ def auction_is_in_allowed_state
#
# id :bigint(8) not null, primary key
# bidder_type :string not null
# bidder_id :integer not null
# auction_id :integer not null
# bidder_id :bigint(8) not null
# auction_id :bigint(8) not null
# aasm_state :string default("pending"), not null
# handled_at :datetime
# created_at :datetime not null
Expand Down
88 changes: 81 additions & 7 deletions app/models/auctify/sale/auction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Auction < Auctify::Sale::Base
}

attr_accessor :winning_bid
attr_accessor :manually_closed_price_check

has_many :bidder_registrations, dependent: :destroy
has_many :bids, through: :bidder_registrations, dependent: :restrict_with_error # destroy them manually first
Expand All @@ -23,11 +24,16 @@ class Auction < Auctify::Sale::Base

belongs_to :winner, polymorphic: true, optional: true
belongs_to :current_winner, polymorphic: true, optional: true
belongs_to :manually_closed_by, polymorphic: true, optional: true
belongs_to :bidding_locked_by, polymorphic: true, optional: true

validates :ends_at,
presence: true

scope :where_current_winner_is, ->(bidder) { where(current_winner: bidder) }
scope :closable_automatically, -> do
where(must_be_closed_manually: false)
end

aasm do
state :offered, initial: true, color: "red"
Expand Down Expand Up @@ -115,6 +121,8 @@ class Auction < Auctify::Sale::Base

validate :buyer_vs_bidding_consistence
validate :forbidden_changes
validate :validate_manually_closed
validate :validate_bidding_locked

after_create :autoregister_bidders
after_save :create_jobs
Expand Down Expand Up @@ -225,7 +233,13 @@ def current_max_price_for(bidder, bids_array: nil)
end

def open_for_bids?
in_sale? && Time.current <= currently_ends_at
return false if bidding_locked_at?

if must_be_closed_manually?
!manually_closed_at && in_sale?
else
in_sale? && Time.current <= currently_ends_at
end
end

def opening_price
Expand All @@ -249,6 +263,23 @@ def auction_prolonging_limit_in_seconds
pack&.auction_prolonging_limit_in_seconds || Auctify.configuration.auction_prolonging_limit_in_seconds
end

def close_manually(by:, price_check:)
if manually_closed_at.nil? && update(manually_closed_at: Time.current, manually_closed_by: by, manually_closed_price_check: price_check)
Auctify::BiddingCloserJob.perform_later(auction_id: id)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tady mi přijde zbytečné to honit přes Job. Rovnou bych dal auction.close_bidding!

true
else
false
end
end

def lock_bidding(by:)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tady budu potřebovat objasnit jaký je rozdíl/použití auction.lick_bidding a auction.bidding_closed. Respektive kdy dochází k unlock_bidding

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lock_bidding a unlock_bidding jsou adminem rucne volane akce z frontendu, ktere slouzi pro mezikrok kontroly dosazene ceny oproti salu pred samotnym manualnim uzavrenim

image

image

!!update(bidding_locked_at: Time.current, bidding_locked_by: by)
end

def unlock_bidding(by:)
!!update(bidding_locked_at: nil, bidding_locked_by: nil)
end

private
def buyer_vs_bidding_consistence
return true if buyer.blank? && sold_price.blank?
Expand Down Expand Up @@ -364,6 +395,38 @@ def create_jobs(force = false)
end
end
end

def validate_manually_closed
return unless will_save_change_to_manually_closed_at?

if manually_closed_at
unless bidding_locked_at?
errors.add(:base, :need_to_lock_bidding_first)
end

if !manually_closed_price_check || manually_closed_price_check.to_i != current_price
errors.add(:base, :current_price_doesnt_match_closing)
end

unless must_be_closed_manually?
errors.add(:base, :sales_not_closed_manually)
end

unless manually_closed_by.is_a?(Folio::Account)
errors.add(:manually_closed_by, :not_allowed)
end
end
end

def validate_bidding_locked
return unless will_save_change_to_bidding_locked_at?

if bidding_locked_at?
unless bidding_locked_by.is_a?(Folio::Account)
errors.add(:bidding_locked_by, :not_allowed)
end
end
end
end
end
end
Expand All @@ -376,16 +439,16 @@ def create_jobs(force = false)
# seller_type :string
# seller_id :integer
# buyer_type :string
# buyer_id :integer
# item_id :integer not null
# buyer_id :bigint(8)
# item_id :bigint(8) not null
# created_at :datetime not null
# updated_at :datetime not null
# type :string default("Auctify::Sale::Base")
# aasm_state :string default("offered"), not null
# offered_price :decimal(, )
# current_price :decimal(, )
# sold_price :decimal(, )
# bid_steps_ladder :json
# offered_price :decimal(12, 2)
# current_price :decimal(12, 2)
# sold_price :decimal(12, 2)
# bid_steps_ladder :jsonb
# reserve_price :decimal(, )
# pack_id :bigint(8)
# ends_at :datetime
Expand All @@ -404,12 +467,23 @@ def create_jobs(force = false)
# current_winner_id :bigint(8)
# buyer_commission_in_percent :integer
# featured :integer
# manually_closed_at :datetime
# manually_closed_by_type :string
# manually_closed_by_id :bigint(8)
# must_be_closed_manually :boolean default(FALSE)
# bidding_locked_at :datetime
# bidding_locked_by_type :string
# bidding_locked_by_id :bigint(8)
#
# Indexes
#
# index_auctify_sales_on_aasm_state (aasm_state)
# index_auctify_sales_on_bidding_locked_by (bidding_locked_by_type,bidding_locked_by_id)
# index_auctify_sales_on_buyer_type_and_buyer_id (buyer_type,buyer_id)
# index_auctify_sales_on_currently_ends_at (currently_ends_at)
# index_auctify_sales_on_featured (featured)
# index_auctify_sales_on_manually_closed_by (manually_closed_by_type,manually_closed_by_id)
# index_auctify_sales_on_must_be_closed_manually (must_be_closed_manually)
# index_auctify_sales_on_pack_id (pack_id)
# index_auctify_sales_on_position (position)
# index_auctify_sales_on_published (published)
Expand Down
66 changes: 57 additions & 9 deletions app/models/auctify/sale/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,50 @@ class Base < ApplicationRecord
scope :not_sold, -> { where(sold_price: nil) }
scope :ordered, -> { order(currently_ends_at: :asc, id: :asc) }

# need auction scopes here because of has_many :sales, class_name: "Auctify::Sale::Base"
scope :auctions_open_for_bids, -> do
where(aasm_state: "in_sale").where("currently_ends_at > ?", Time.current)
sql = <<~SQL
(
auctify_sales.must_be_closed_manually = ?
AND
auctify_sales.aasm_state = ?
) OR (
auctify_sales.must_be_closed_manually = ?
AND
auctify_sales.aasm_state = ?
AND
auctify_sales.currently_ends_at > ?
)
SQL

where(sql,
true,
"in_sale",
false,
"in_sale",
Time.current)
end

scope :auctions_finished, -> do
where.not(aasm_state: %w[offered accepted refused]).where("currently_ends_at < ?", Time.current)
sql = <<~SQL
(
auctify_sales.must_be_closed_manually = ?
AND
auctify_sales.aasm_state IN (?)
) OR (
auctify_sales.must_be_closed_manually = ?
AND
auctify_sales.aasm_state NOT IN (?)
AND
auctify_sales.currently_ends_at < ?
)
SQL

where(sql,
true,
%w[bidding_ended auctioned_successfully auctioned_unsuccessfully sold not_sold],
false,
%w[offered accepted refused],
Time.current)
end

scope :latest_published_by_item, -> { joins(latest_published_sales_by_item_subtable.join_sources) }
Expand Down Expand Up @@ -172,16 +209,16 @@ def slug_candidates
# seller_type :string
# seller_id :integer
# buyer_type :string
# buyer_id :integer
# item_id :integer not null
# buyer_id :bigint(8)
# item_id :bigint(8) not null
# created_at :datetime not null
# updated_at :datetime not null
# type :string default("Auctify::Sale::Base")
# aasm_state :string default("offered"), not null
# offered_price :decimal(, )
# current_price :decimal(, )
# sold_price :decimal(, )
# bid_steps_ladder :json
# offered_price :decimal(12, 2)
# current_price :decimal(12, 2)
# sold_price :decimal(12, 2)
# bid_steps_ladder :jsonb
# reserve_price :decimal(, )
# pack_id :bigint(8)
# ends_at :datetime
Expand All @@ -200,12 +237,23 @@ def slug_candidates
# current_winner_id :bigint(8)
# buyer_commission_in_percent :integer
# featured :integer
# manually_closed_at :datetime
# manually_closed_by_type :string
# manually_closed_by_id :bigint(8)
# must_be_closed_manually :boolean default(FALSE)
# bidding_locked_at :datetime
# bidding_locked_by_type :string
# bidding_locked_by_id :bigint(8)
#
# Indexes
#
# index_auctify_sales_on_aasm_state (aasm_state)
# index_auctify_sales_on_bidding_locked_by (bidding_locked_by_type,bidding_locked_by_id)
# index_auctify_sales_on_buyer_type_and_buyer_id (buyer_type,buyer_id)
# index_auctify_sales_on_currently_ends_at (currently_ends_at)
# index_auctify_sales_on_featured (featured)
# index_auctify_sales_on_manually_closed_by (manually_closed_by_type,manually_closed_by_id)
# index_auctify_sales_on_must_be_closed_manually (must_be_closed_manually)
# index_auctify_sales_on_pack_id (pack_id)
# index_auctify_sales_on_position (position)
# index_auctify_sales_on_published (published)
Expand Down
Loading