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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +229 -0
- data/Rakefile +34 -0
- data/app/assets/config/auctify_manifest.js +1 -0
- data/app/assets/javascripts/folio/console/main_app.coffee +1 -0
- data/app/assets/stylesheets/auctify/application.css +15 -0
- data/app/assets/stylesheets/auctify/bidder_registrations.css +4 -0
- data/app/assets/stylesheets/auctify/bids.css +4 -0
- data/app/assets/stylesheets/auctify/sales.css +4 -0
- data/app/assets/stylesheets/auctify/sales_packs.css +4 -0
- data/app/assets/stylesheets/folio/console/_main_app.sass +1 -0
- data/app/assets/stylesheets/scaffold.css +80 -0
- data/app/cells/folio/console/atoms/previews/main_app.coffee +1 -0
- data/app/concerns/auctify/behavior/base.rb +20 -0
- data/app/concerns/auctify/behavior/buyer.rb +16 -0
- data/app/concerns/auctify/behavior/item.rb +27 -0
- data/app/concerns/auctify/behavior/seller.rb +36 -0
- data/app/concerns/auctify/sale/auction_callbacks.rb +44 -0
- data/app/controllers/auctify/api/v1/auctions_controller.rb +61 -0
- data/app/controllers/auctify/api/v1/base_controller.rb +22 -0
- data/app/controllers/auctify/api/v1/console/bids_controller.rb +37 -0
- data/app/controllers/auctify/application_controller.rb +8 -0
- data/app/controllers/auctify/bidder_registrations_controller.rb +65 -0
- data/app/controllers/auctify/bids_controller.rb +64 -0
- data/app/controllers/auctify/sales_controller.rb +75 -0
- data/app/controllers/auctify/sales_packs_controller.rb +75 -0
- data/app/helpers/auctify/application_helper.rb +10 -0
- data/app/helpers/auctify/bidder_registrations_helper.rb +6 -0
- data/app/helpers/auctify/bids_helper.rb +6 -0
- data/app/helpers/auctify/sales_helper.rb +15 -0
- data/app/helpers/auctify/sales_packs_helper.rb +6 -0
- data/app/jobs/auctify/application_job.rb +6 -0
- data/app/jobs/auctify/bidding_closer_job.rb +23 -0
- data/app/jobs/auctify/bidding_is_close_to_end_notifier_job.rb +29 -0
- data/app/mailers/auctify/application_mailer.rb +8 -0
- data/app/models/auctify/application_record.rb +14 -0
- data/app/models/auctify/behaviors.rb +27 -0
- data/app/models/auctify/bid.rb +72 -0
- data/app/models/auctify/bidder_registration.rb +84 -0
- data/app/models/auctify/sale/auction.rb +404 -0
- data/app/models/auctify/sale/base.rb +210 -0
- data/app/models/auctify/sale/retail.rb +97 -0
- data/app/models/auctify/sales_pack.rb +101 -0
- data/app/serializers/auctify/sale/auction_serializer.rb +46 -0
- data/app/services/auctify/bids_appender.rb +241 -0
- data/app/services/auctify/service_base.rb +78 -0
- data/app/types/auctify/minimal_bids_ladder_type.rb +77 -0
- data/app/views/auctify/api/v1/console/bids/index.html.erb +1 -0
- data/app/views/auctify/api/v1/console/bids/show.html.erb +1 -0
- data/app/views/auctify/bidder_registrations/_form.html.erb +27 -0
- data/app/views/auctify/bidder_registrations/edit.html.erb +6 -0
- data/app/views/auctify/bidder_registrations/index.html.erb +33 -0
- data/app/views/auctify/bidder_registrations/new.html.erb +5 -0
- data/app/views/auctify/bidder_registrations/show.html.erb +24 -0
- data/app/views/auctify/bids/_form.html.erb +32 -0
- data/app/views/auctify/bids/edit.html.erb +6 -0
- data/app/views/auctify/bids/index.html.erb +31 -0
- data/app/views/auctify/bids/new.html.erb +5 -0
- data/app/views/auctify/bids/show.html.erb +19 -0
- data/app/views/auctify/sales/_form.html.erb +32 -0
- data/app/views/auctify/sales/edit.html.erb +6 -0
- data/app/views/auctify/sales/index.html.erb +36 -0
- data/app/views/auctify/sales/new.html.erb +5 -0
- data/app/views/auctify/sales/show.html.erb +19 -0
- data/app/views/auctify/sales_packs/_form.html.erb +57 -0
- data/app/views/auctify/sales_packs/edit.html.erb +6 -0
- data/app/views/auctify/sales_packs/index.html.erb +40 -0
- data/app/views/auctify/sales_packs/new.html.erb +5 -0
- data/app/views/auctify/sales_packs/show.html.erb +44 -0
- data/app/views/layouts/auctify/application.html.erb +15 -0
- data/config/locales/auctify.cs.yml +121 -0
- data/config/locales/default.cs.yml +220 -0
- data/config/locales/default.en.yml +215 -0
- data/config/routes.rb +23 -0
- data/db/migrate/20210212163000_create_auctify_sales.rb +16 -0
- data/db/migrate/20210216151350_add_type_to_auctify_sales.rb +7 -0
- data/db/migrate/20210217101246_add_state_to_auctify_sales.rb +7 -0
- data/db/migrate/20210217110819_add_prices_to_auctify_sales.rb +9 -0
- data/db/migrate/20210222103031_add_published_at_to_auctify_sale.rb +7 -0
- data/db/migrate/20210223090240_create_auctify_bidder_registrations.rb +15 -0
- data/db/migrate/20210223152139_create_auctify_bids.rb +13 -0
- data/db/migrate/20210225103227_add_bid_steps_ladder_to_auctify_sales.rb +7 -0
- data/db/migrate/20210412114319_add_reserve_price_to_auctify_sales.rb +7 -0
- data/db/migrate/20210416140603_create_auctify_sales_packs.rb +17 -0
- data/db/migrate/20210416141111_add_pack_reference_to_auctify_sales.rb +7 -0
- data/db/migrate/20210419075003_add_ends_at_to_auctify_sales.rb +7 -0
- data/db/migrate/20210419082800_add_position_to_auctify_sales.rb +8 -0
- data/db/migrate/20210419083321_add_indexes.rb +12 -0
- data/db/migrate/20210421093652_add_number_to_auctify_sales.rb +7 -0
- data/db/migrate/20210423071326_removing_polymorphic_assoc_sale_to_item.rb +7 -0
- data/db/migrate/20210423071534_add_currently_ends_at_to_auctify_sales.rb +7 -0
- data/db/migrate/20210427121353_change_column_published_on_auctify_sale.rb +15 -0
- data/db/migrate/20210427122315_add_featured_to_auctify_sales.rb +7 -0
- data/db/migrate/20210428045651_add_sales_bool_indices.rb +9 -0
- data/db/migrate/20210428064615_allow_seller_to_be_nil.rb +13 -0
- data/db/migrate/20210429051508_add_sales_pack_start_and_end.rb +15 -0
- data/db/migrate/20210430080419_add_sales_slug.rb +11 -0
- data/db/migrate/20210503095704_add_cancelled_to_auctify_bids.rb +7 -0
- data/db/migrate/20210504072041_add_commission_and_contract_to_auctify_sales.rb +8 -0
- data/db/migrate/20210506064100_add_winner_to_auctify_sales.rb +7 -0
- data/db/migrate/20210506072438_add_bids_count_to_auctify_sales.rb +7 -0
- data/db/migrate/20210507085845_rename_bids_count_to_applied_bids_count.rb +7 -0
- data/db/migrate/20210510063146_add_dont_confirm_bids_to_registrations.rb +7 -0
- data/db/migrate/20210511081556_add_index_on_cancelled_on.rb +7 -0
- data/db/migrate/20210512162942_add_sold_at_to_auctify_sales.rb +7 -0
- data/db/migrate/20210607113440_add_commission_in_percent_to_sales_packs.rb +7 -0
- data/db/migrate/20210617062509_add_index_to_auctify_sales_currently_ends_at.rb +7 -0
- data/db/migrate/20210625125732_add_current_winner_to_auctify_sales.rb +7 -0
- data/lib/auctify.rb +7 -0
- data/lib/auctify/configuration.rb +43 -0
- data/lib/auctify/engine.rb +25 -0
- data/lib/auctify/version.rb +5 -0
- data/lib/tasks/auctify_tasks.rake +23 -0
- metadata +442 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Auctify
|
|
4
|
+
module Sale
|
|
5
|
+
class Base < ApplicationRecord
|
|
6
|
+
include Folio::FriendlyId
|
|
7
|
+
include Folio::Positionable
|
|
8
|
+
include Folio::Featurable::Basic
|
|
9
|
+
include Folio::Publishable::Basic
|
|
10
|
+
|
|
11
|
+
self.table_name = "auctify_sales"
|
|
12
|
+
|
|
13
|
+
attribute :bid_steps_ladder, MinimalBidsLadderType.new
|
|
14
|
+
|
|
15
|
+
include Auctify::Behavior::Base
|
|
16
|
+
|
|
17
|
+
belongs_to :seller, polymorphic: true, optional: true
|
|
18
|
+
belongs_to :buyer, polymorphic: true, optional: true
|
|
19
|
+
# added on usage of `auctify_as :item` => belongs_to :item, class_name: ???
|
|
20
|
+
belongs_to :pack, class_name: "Auctify::SalesPack", inverse_of: :sales, optional: true, counter_cache: :sales_count
|
|
21
|
+
|
|
22
|
+
validate :valid_seller
|
|
23
|
+
validate :valid_item
|
|
24
|
+
validate :valid_buyer
|
|
25
|
+
|
|
26
|
+
validates :offered_price,
|
|
27
|
+
:current_price,
|
|
28
|
+
:sold_price,
|
|
29
|
+
:reserve_price,
|
|
30
|
+
numericality: { greater_than_or_equal_to: 0 },
|
|
31
|
+
allow_nil: true
|
|
32
|
+
|
|
33
|
+
validate :validate_offered_price_when_published
|
|
34
|
+
|
|
35
|
+
scope :not_sold, -> { where(sold_price: nil) }
|
|
36
|
+
|
|
37
|
+
# need auction scopes here because of has_many :sales, class_name: "Auctify::Sale::Base"
|
|
38
|
+
scope :auctions_open_for_bids, -> do
|
|
39
|
+
where(aasm_state: "in_sale").where("currently_ends_at > ?", Time.current)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
scope :auctions_finished, -> do
|
|
43
|
+
where.not(aasm_state: %w[offered accepted refused]).where("currently_ends_at < ?", Time.current)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
scope :latest_published_by_item, -> { joins(latest_published_sales_by_item_subtable.join_sources) }
|
|
47
|
+
|
|
48
|
+
delegate :to_label,
|
|
49
|
+
to: :item
|
|
50
|
+
|
|
51
|
+
def self.latest_published_sales_by_item_subtable
|
|
52
|
+
# see https://www.salsify.com/blog/engineering/most-recent-by-group-in-rails
|
|
53
|
+
sales_table = self.arel_table
|
|
54
|
+
|
|
55
|
+
latest_sales_query = sales_table.project(sales_table[:item_id],
|
|
56
|
+
sales_table[:id].maximum.as("latest_sale_id"))
|
|
57
|
+
.where(sales_table[:published].eq(true))
|
|
58
|
+
.group(sales_table[:item_id])
|
|
59
|
+
|
|
60
|
+
latest_sales_table = Arel::Table.new(latest_sales_query).alias(:latest_sales) # need to perform join
|
|
61
|
+
|
|
62
|
+
sales_table.join(latest_sales_query.as(latest_sales_table.name.to_s), Arel::Nodes::InnerJoin)
|
|
63
|
+
.on(sales_table[:id].eq(latest_sales_table[:latest_sale_id]))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def initialize(*args)
|
|
67
|
+
super
|
|
68
|
+
|
|
69
|
+
self.bid_steps_ladder = configuration.default_bid_steps_ladder if bid_steps_ladder.blank?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# need to cover wrong class of item before assigning
|
|
73
|
+
def item=(item)
|
|
74
|
+
@item = item
|
|
75
|
+
valid_item
|
|
76
|
+
|
|
77
|
+
super if errors.blank?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
[:seller, :buyer, :item].each do |behavior|
|
|
81
|
+
define_method("#{behavior}_auctify_id=") do |auctify_id|
|
|
82
|
+
self.send("#{behavior}=", object_from_auctify_id(auctify_id))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
define_method("#{behavior}_auctify_id") do
|
|
86
|
+
self.send("#{behavior}")&.auctify_id
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def publish!
|
|
91
|
+
self.published = true
|
|
92
|
+
save
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def auctioneer_commission
|
|
96
|
+
return nil if sold_price.nil?
|
|
97
|
+
|
|
98
|
+
percent = commission_in_percent \
|
|
99
|
+
|| (pack&.commission_in_percent) \
|
|
100
|
+
|| Auctify.configuration.auctioneer_commission_in_percent
|
|
101
|
+
|
|
102
|
+
sold_price * percent * 0.01
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
def valid_seller
|
|
107
|
+
return true if seller_id.blank?
|
|
108
|
+
|
|
109
|
+
db_seller = db_presence_of(seller)
|
|
110
|
+
|
|
111
|
+
errors.add(:seller, :required) unless db_seller.present?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def valid_item
|
|
115
|
+
item = @item || self.item
|
|
116
|
+
errors.delete(:item)
|
|
117
|
+
|
|
118
|
+
db_item = db_presence_of(item)
|
|
119
|
+
if db_item.present?
|
|
120
|
+
errors.add(:item, :already_on_sale_in_sales_pack, sale_pack_title: pack.title) if pack && pack.sales
|
|
121
|
+
.where(item: db_item)
|
|
122
|
+
.where.not(id: self.id)
|
|
123
|
+
.exists?
|
|
124
|
+
else
|
|
125
|
+
# Rails will add "required" on item.nil? automagically (and after this validation) but not for non-persisted item
|
|
126
|
+
# we do not allow creating sale along with item, so this trying to cover that
|
|
127
|
+
errors.add(:item, :required) unless item.blank? || errors.details[:item].include?({ error: :blank })
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def valid_buyer
|
|
132
|
+
db_buyer = db_presence_of(buyer)
|
|
133
|
+
errors.add(:buyer, :required) if buyer.present? && db_buyer.blank?
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def db_presence_of(record)
|
|
137
|
+
return nil if record.blank?
|
|
138
|
+
|
|
139
|
+
record.reload
|
|
140
|
+
rescue ActiveRecord::RecordNotFound
|
|
141
|
+
nil
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def configuration
|
|
145
|
+
Auctify.configuration
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def validate_offered_price_when_published
|
|
149
|
+
if published? && offered_price.blank?
|
|
150
|
+
errors.add(:offered_price, :required_for_published)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def slug_candidates
|
|
155
|
+
base = try(:item).try(:title).presence || self.class.model_name.human
|
|
156
|
+
year = Time.zone.now.year
|
|
157
|
+
[base, "#{base}-#{year}"] + 9.times.map { |i| "#{base}-#{year}-#{i + 2}" }
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# == Schema Information
|
|
164
|
+
#
|
|
165
|
+
# Table name: auctify_sales
|
|
166
|
+
#
|
|
167
|
+
# id :bigint(8) not null, primary key
|
|
168
|
+
# seller_type :string
|
|
169
|
+
# seller_id :integer
|
|
170
|
+
# buyer_type :string
|
|
171
|
+
# buyer_id :integer
|
|
172
|
+
# item_id :integer not null
|
|
173
|
+
# created_at :datetime not null
|
|
174
|
+
# updated_at :datetime not null
|
|
175
|
+
# type :string default("Auctify::Sale::Base")
|
|
176
|
+
# aasm_state :string default("offered"), not null
|
|
177
|
+
# offered_price :decimal(, )
|
|
178
|
+
# current_price :decimal(, )
|
|
179
|
+
# sold_price :decimal(, )
|
|
180
|
+
# bid_steps_ladder :json
|
|
181
|
+
# reserve_price :decimal(, )
|
|
182
|
+
# pack_id :bigint(8)
|
|
183
|
+
# ends_at :datetime
|
|
184
|
+
# position :integer
|
|
185
|
+
# number :string
|
|
186
|
+
# currently_ends_at :datetime
|
|
187
|
+
# published :boolean default(FALSE)
|
|
188
|
+
# featured :boolean default(FALSE)
|
|
189
|
+
# slug :string
|
|
190
|
+
# contract_number :string
|
|
191
|
+
# commission_in_percent :integer
|
|
192
|
+
# winner_type :string
|
|
193
|
+
# winner_id :bigint(8)
|
|
194
|
+
# applied_bids_count :integer default(0)
|
|
195
|
+
# sold_at :datetime
|
|
196
|
+
# current_winner_type :string
|
|
197
|
+
# current_winner_id :bigint(8)
|
|
198
|
+
#
|
|
199
|
+
# Indexes
|
|
200
|
+
#
|
|
201
|
+
# index_auctify_sales_on_buyer_type_and_buyer_id (buyer_type,buyer_id)
|
|
202
|
+
# index_auctify_sales_on_currently_ends_at (currently_ends_at)
|
|
203
|
+
# index_auctify_sales_on_featured (featured)
|
|
204
|
+
# index_auctify_sales_on_pack_id (pack_id)
|
|
205
|
+
# index_auctify_sales_on_position (position)
|
|
206
|
+
# index_auctify_sales_on_published (published)
|
|
207
|
+
# index_auctify_sales_on_seller_type_and_seller_id (seller_type,seller_id)
|
|
208
|
+
# index_auctify_sales_on_slug (slug) UNIQUE
|
|
209
|
+
# index_auctify_sales_on_winner_type_and_winner_id (winner_type,winner_id)
|
|
210
|
+
#
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Auctify
|
|
4
|
+
module Sale
|
|
5
|
+
class Retail < Auctify::Sale::Base
|
|
6
|
+
include AASM
|
|
7
|
+
|
|
8
|
+
aasm do
|
|
9
|
+
state :offered, initial: true, color: "red"
|
|
10
|
+
state :accepted, color: "red"
|
|
11
|
+
state :refused, color: "dark"
|
|
12
|
+
state :in_sale, color: "yellow"
|
|
13
|
+
state :sold, color: "green"
|
|
14
|
+
state :not_sold, color: "dark"
|
|
15
|
+
state :cancelled, color: "red"
|
|
16
|
+
|
|
17
|
+
event :accept_offer do
|
|
18
|
+
transitions from: :offered, to: :accepted
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
event :refuse_offer do
|
|
22
|
+
transitions from: :offered, to: :refused
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
event :start_sale do
|
|
26
|
+
transitions from: :accepted, to: :in_sale
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
event :sell do
|
|
30
|
+
transitions from: :in_sale, to: :sold
|
|
31
|
+
after do |*args| # TODO: sold_at
|
|
32
|
+
params = args.first # expecting keys :buyer, :price
|
|
33
|
+
self.buyer = params[:buyer]
|
|
34
|
+
self.sold_price = params[:price]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
event :end_sale do
|
|
39
|
+
transitions from: :in_sale, to: :not_sold
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
event :cancel do
|
|
43
|
+
transitions from: [:offered, :accepted], to: :cancelled
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# == Schema Information
|
|
51
|
+
#
|
|
52
|
+
# Table name: auctify_sales
|
|
53
|
+
#
|
|
54
|
+
# id :bigint(8) not null, primary key
|
|
55
|
+
# seller_type :string
|
|
56
|
+
# seller_id :integer
|
|
57
|
+
# buyer_type :string
|
|
58
|
+
# buyer_id :integer
|
|
59
|
+
# item_id :integer not null
|
|
60
|
+
# created_at :datetime not null
|
|
61
|
+
# updated_at :datetime not null
|
|
62
|
+
# type :string default("Auctify::Sale::Base")
|
|
63
|
+
# aasm_state :string default("offered"), not null
|
|
64
|
+
# offered_price :decimal(, )
|
|
65
|
+
# current_price :decimal(, )
|
|
66
|
+
# sold_price :decimal(, )
|
|
67
|
+
# bid_steps_ladder :json
|
|
68
|
+
# reserve_price :decimal(, )
|
|
69
|
+
# pack_id :bigint(8)
|
|
70
|
+
# ends_at :datetime
|
|
71
|
+
# position :integer
|
|
72
|
+
# number :string
|
|
73
|
+
# currently_ends_at :datetime
|
|
74
|
+
# published :boolean default(FALSE)
|
|
75
|
+
# featured :boolean default(FALSE)
|
|
76
|
+
# slug :string
|
|
77
|
+
# contract_number :string
|
|
78
|
+
# commission_in_percent :integer
|
|
79
|
+
# winner_type :string
|
|
80
|
+
# winner_id :bigint(8)
|
|
81
|
+
# applied_bids_count :integer default(0)
|
|
82
|
+
# sold_at :datetime
|
|
83
|
+
# current_winner_type :string
|
|
84
|
+
# current_winner_id :bigint(8)
|
|
85
|
+
#
|
|
86
|
+
# Indexes
|
|
87
|
+
#
|
|
88
|
+
# index_auctify_sales_on_buyer_type_and_buyer_id (buyer_type,buyer_id)
|
|
89
|
+
# index_auctify_sales_on_currently_ends_at (currently_ends_at)
|
|
90
|
+
# index_auctify_sales_on_featured (featured)
|
|
91
|
+
# index_auctify_sales_on_pack_id (pack_id)
|
|
92
|
+
# index_auctify_sales_on_position (position)
|
|
93
|
+
# index_auctify_sales_on_published (published)
|
|
94
|
+
# index_auctify_sales_on_seller_type_and_seller_id (seller_type,seller_id)
|
|
95
|
+
# index_auctify_sales_on_slug (slug) UNIQUE
|
|
96
|
+
# index_auctify_sales_on_winner_type_and_winner_id (winner_type,winner_id)
|
|
97
|
+
#
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Auctify
|
|
4
|
+
class SalesPack < ApplicationRecord
|
|
5
|
+
include Folio::FriendlyId
|
|
6
|
+
include Folio::HasAttachments
|
|
7
|
+
include Folio::Positionable
|
|
8
|
+
include Folio::Publishable::Basic
|
|
9
|
+
include Folio::Sitemap::Base
|
|
10
|
+
|
|
11
|
+
has_many :sales, class_name: "Auctify::Sale::Base", foreign_key: :pack_id, inverse_of: :pack, dependent: :restrict_with_error
|
|
12
|
+
has_many :items, through: :sales
|
|
13
|
+
|
|
14
|
+
validates :title,
|
|
15
|
+
presence: true,
|
|
16
|
+
uniqueness: true
|
|
17
|
+
|
|
18
|
+
validates :start_date,
|
|
19
|
+
:end_date,
|
|
20
|
+
presence: true
|
|
21
|
+
|
|
22
|
+
validates :sales_interval,
|
|
23
|
+
numericality: { greater_than: 0, less_than: 240 }
|
|
24
|
+
|
|
25
|
+
validates :sales_beginning_hour,
|
|
26
|
+
numericality: { greater_than_or_equal: 0, less_than: 24 }
|
|
27
|
+
|
|
28
|
+
validates :sales_beginning_minutes,
|
|
29
|
+
numericality: { greater_than_or_equal: 0, less_than: 60 }
|
|
30
|
+
|
|
31
|
+
validate :validate_start_and_end_dates
|
|
32
|
+
|
|
33
|
+
scope :ordered, -> { order(start_date: :desc, id: :desc) }
|
|
34
|
+
|
|
35
|
+
pg_search_scope :by_query,
|
|
36
|
+
against: %i[title],
|
|
37
|
+
ignoring: :accents,
|
|
38
|
+
using: { tsearch: { prefix: true } }
|
|
39
|
+
|
|
40
|
+
after_initialize :set_commission
|
|
41
|
+
|
|
42
|
+
def to_label
|
|
43
|
+
title
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def dates_to_label
|
|
47
|
+
if start_date && end_date
|
|
48
|
+
if start_date.year == end_date.year
|
|
49
|
+
if start_date.month == end_date.month
|
|
50
|
+
"#{start_date.strftime('%-d.')}–#{end_date.strftime('%-d. %-m. %y')}"
|
|
51
|
+
else
|
|
52
|
+
"#{start_date.strftime('%-d. %-m.')} – #{end_date.strftime('%-d. %-m. %y')}"
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
"#{start_date.strftime('%-d. %-m. %y')} – #{end_date.strftime('%-d. %-m. %y')}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
def validate_start_and_end_dates
|
|
62
|
+
if start_date.present? && end_date.present? && start_date > end_date
|
|
63
|
+
errors.add(:end_date, :smaller_than_start_date)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def set_commission
|
|
68
|
+
return if self.commission_in_percent.present?
|
|
69
|
+
|
|
70
|
+
self.commission_in_percent = Auctify.configuration.auctioneer_commission_in_percent
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# == Schema Information
|
|
76
|
+
#
|
|
77
|
+
# Table name: auctify_sales_packs
|
|
78
|
+
#
|
|
79
|
+
# id :bigint(8) not null, primary key
|
|
80
|
+
# title :string
|
|
81
|
+
# description :text
|
|
82
|
+
# position :integer default(0)
|
|
83
|
+
# slug :string
|
|
84
|
+
# place :string
|
|
85
|
+
# published :boolean default(FALSE)
|
|
86
|
+
# created_at :datetime not null
|
|
87
|
+
# updated_at :datetime not null
|
|
88
|
+
# sales_count :integer default(0)
|
|
89
|
+
# start_date :date
|
|
90
|
+
# end_date :date
|
|
91
|
+
# sales_interval :integer default(3)
|
|
92
|
+
# sales_beginning_hour :integer default(20)
|
|
93
|
+
# sales_beginning_minutes :integer default(0)
|
|
94
|
+
# commission_in_percent :integer
|
|
95
|
+
#
|
|
96
|
+
# Indexes
|
|
97
|
+
#
|
|
98
|
+
# index_auctify_sales_packs_on_position (position)
|
|
99
|
+
# index_auctify_sales_packs_on_published (published)
|
|
100
|
+
# index_auctify_sales_packs_on_slug (slug) UNIQUE
|
|
101
|
+
#
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Auctify::Sale::AuctionSerializer
|
|
4
|
+
include FastJsonapi::ObjectSerializer
|
|
5
|
+
|
|
6
|
+
attributes :id,
|
|
7
|
+
:current_price,
|
|
8
|
+
:current_minimal_bid,
|
|
9
|
+
:currently_ends_at,
|
|
10
|
+
:ends_at,
|
|
11
|
+
:open_for_bids?
|
|
12
|
+
|
|
13
|
+
attribute :current_winner do |auction|
|
|
14
|
+
winner = auction.current_winner
|
|
15
|
+
{
|
|
16
|
+
id: winner.id,
|
|
17
|
+
auctify_id: winner.auctify_id,
|
|
18
|
+
to_label: winner.to_label
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# assert_equal auction.current_winner.id, json_attributes["current_winner"]["id"]
|
|
27
|
+
# assert_equal auction.current_winner.auctify_id, json_attributes["current_winner"]["auctify_id"]
|
|
28
|
+
# assert_equal auction.current_winner.to_label, json_attributes["current_winner"]["to_label"]
|
|
29
|
+
# assert_equal auction.current_price, json_attributes["current_price"]
|
|
30
|
+
# assert_equal auction.current_minimal_bid, json_attributes["current_minimal_bid"]
|
|
31
|
+
# assert_equal auction.ends_at, json_attributes["ends_at"]
|
|
32
|
+
# assert_equal auction.currently_ends_at, json_attributes["currently_ends_at"]
|
|
33
|
+
# assert_equal auction.open_for_bids?, json_attributes["open_for_bids?"]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# attributes :name, :year
|
|
37
|
+
# has_many :actors
|
|
38
|
+
# belongs_to :owner, record_type: :user
|
|
39
|
+
# belongs_to :movie_ty
|
|
40
|
+
# # link :url do |object|
|
|
41
|
+
# # Rails.application
|
|
42
|
+
# # .routes
|
|
43
|
+
# # .url_helpers
|
|
44
|
+
# # .url_for([object, only_path: true])
|
|
45
|
+
# # end
|
|
46
|
+
end
|