auctify 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +229 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/config/auctify_manifest.js +1 -0
  6. data/app/assets/javascripts/folio/console/main_app.coffee +1 -0
  7. data/app/assets/stylesheets/auctify/application.css +15 -0
  8. data/app/assets/stylesheets/auctify/bidder_registrations.css +4 -0
  9. data/app/assets/stylesheets/auctify/bids.css +4 -0
  10. data/app/assets/stylesheets/auctify/sales.css +4 -0
  11. data/app/assets/stylesheets/auctify/sales_packs.css +4 -0
  12. data/app/assets/stylesheets/folio/console/_main_app.sass +1 -0
  13. data/app/assets/stylesheets/scaffold.css +80 -0
  14. data/app/cells/folio/console/atoms/previews/main_app.coffee +1 -0
  15. data/app/concerns/auctify/behavior/base.rb +20 -0
  16. data/app/concerns/auctify/behavior/buyer.rb +16 -0
  17. data/app/concerns/auctify/behavior/item.rb +27 -0
  18. data/app/concerns/auctify/behavior/seller.rb +36 -0
  19. data/app/concerns/auctify/sale/auction_callbacks.rb +44 -0
  20. data/app/controllers/auctify/api/v1/auctions_controller.rb +61 -0
  21. data/app/controllers/auctify/api/v1/base_controller.rb +22 -0
  22. data/app/controllers/auctify/api/v1/console/bids_controller.rb +37 -0
  23. data/app/controllers/auctify/application_controller.rb +8 -0
  24. data/app/controllers/auctify/bidder_registrations_controller.rb +65 -0
  25. data/app/controllers/auctify/bids_controller.rb +64 -0
  26. data/app/controllers/auctify/sales_controller.rb +75 -0
  27. data/app/controllers/auctify/sales_packs_controller.rb +75 -0
  28. data/app/helpers/auctify/application_helper.rb +10 -0
  29. data/app/helpers/auctify/bidder_registrations_helper.rb +6 -0
  30. data/app/helpers/auctify/bids_helper.rb +6 -0
  31. data/app/helpers/auctify/sales_helper.rb +15 -0
  32. data/app/helpers/auctify/sales_packs_helper.rb +6 -0
  33. data/app/jobs/auctify/application_job.rb +6 -0
  34. data/app/jobs/auctify/bidding_closer_job.rb +23 -0
  35. data/app/jobs/auctify/bidding_is_close_to_end_notifier_job.rb +29 -0
  36. data/app/mailers/auctify/application_mailer.rb +8 -0
  37. data/app/models/auctify/application_record.rb +14 -0
  38. data/app/models/auctify/behaviors.rb +27 -0
  39. data/app/models/auctify/bid.rb +72 -0
  40. data/app/models/auctify/bidder_registration.rb +84 -0
  41. data/app/models/auctify/sale/auction.rb +404 -0
  42. data/app/models/auctify/sale/base.rb +210 -0
  43. data/app/models/auctify/sale/retail.rb +97 -0
  44. data/app/models/auctify/sales_pack.rb +101 -0
  45. data/app/serializers/auctify/sale/auction_serializer.rb +46 -0
  46. data/app/services/auctify/bids_appender.rb +241 -0
  47. data/app/services/auctify/service_base.rb +78 -0
  48. data/app/types/auctify/minimal_bids_ladder_type.rb +77 -0
  49. data/app/views/auctify/api/v1/console/bids/index.html.erb +1 -0
  50. data/app/views/auctify/api/v1/console/bids/show.html.erb +1 -0
  51. data/app/views/auctify/bidder_registrations/_form.html.erb +27 -0
  52. data/app/views/auctify/bidder_registrations/edit.html.erb +6 -0
  53. data/app/views/auctify/bidder_registrations/index.html.erb +33 -0
  54. data/app/views/auctify/bidder_registrations/new.html.erb +5 -0
  55. data/app/views/auctify/bidder_registrations/show.html.erb +24 -0
  56. data/app/views/auctify/bids/_form.html.erb +32 -0
  57. data/app/views/auctify/bids/edit.html.erb +6 -0
  58. data/app/views/auctify/bids/index.html.erb +31 -0
  59. data/app/views/auctify/bids/new.html.erb +5 -0
  60. data/app/views/auctify/bids/show.html.erb +19 -0
  61. data/app/views/auctify/sales/_form.html.erb +32 -0
  62. data/app/views/auctify/sales/edit.html.erb +6 -0
  63. data/app/views/auctify/sales/index.html.erb +36 -0
  64. data/app/views/auctify/sales/new.html.erb +5 -0
  65. data/app/views/auctify/sales/show.html.erb +19 -0
  66. data/app/views/auctify/sales_packs/_form.html.erb +57 -0
  67. data/app/views/auctify/sales_packs/edit.html.erb +6 -0
  68. data/app/views/auctify/sales_packs/index.html.erb +40 -0
  69. data/app/views/auctify/sales_packs/new.html.erb +5 -0
  70. data/app/views/auctify/sales_packs/show.html.erb +44 -0
  71. data/app/views/layouts/auctify/application.html.erb +15 -0
  72. data/config/locales/auctify.cs.yml +121 -0
  73. data/config/locales/default.cs.yml +220 -0
  74. data/config/locales/default.en.yml +215 -0
  75. data/config/routes.rb +23 -0
  76. data/db/migrate/20210212163000_create_auctify_sales.rb +16 -0
  77. data/db/migrate/20210216151350_add_type_to_auctify_sales.rb +7 -0
  78. data/db/migrate/20210217101246_add_state_to_auctify_sales.rb +7 -0
  79. data/db/migrate/20210217110819_add_prices_to_auctify_sales.rb +9 -0
  80. data/db/migrate/20210222103031_add_published_at_to_auctify_sale.rb +7 -0
  81. data/db/migrate/20210223090240_create_auctify_bidder_registrations.rb +15 -0
  82. data/db/migrate/20210223152139_create_auctify_bids.rb +13 -0
  83. data/db/migrate/20210225103227_add_bid_steps_ladder_to_auctify_sales.rb +7 -0
  84. data/db/migrate/20210412114319_add_reserve_price_to_auctify_sales.rb +7 -0
  85. data/db/migrate/20210416140603_create_auctify_sales_packs.rb +17 -0
  86. data/db/migrate/20210416141111_add_pack_reference_to_auctify_sales.rb +7 -0
  87. data/db/migrate/20210419075003_add_ends_at_to_auctify_sales.rb +7 -0
  88. data/db/migrate/20210419082800_add_position_to_auctify_sales.rb +8 -0
  89. data/db/migrate/20210419083321_add_indexes.rb +12 -0
  90. data/db/migrate/20210421093652_add_number_to_auctify_sales.rb +7 -0
  91. data/db/migrate/20210423071326_removing_polymorphic_assoc_sale_to_item.rb +7 -0
  92. data/db/migrate/20210423071534_add_currently_ends_at_to_auctify_sales.rb +7 -0
  93. data/db/migrate/20210427121353_change_column_published_on_auctify_sale.rb +15 -0
  94. data/db/migrate/20210427122315_add_featured_to_auctify_sales.rb +7 -0
  95. data/db/migrate/20210428045651_add_sales_bool_indices.rb +9 -0
  96. data/db/migrate/20210428064615_allow_seller_to_be_nil.rb +13 -0
  97. data/db/migrate/20210429051508_add_sales_pack_start_and_end.rb +15 -0
  98. data/db/migrate/20210430080419_add_sales_slug.rb +11 -0
  99. data/db/migrate/20210503095704_add_cancelled_to_auctify_bids.rb +7 -0
  100. data/db/migrate/20210504072041_add_commission_and_contract_to_auctify_sales.rb +8 -0
  101. data/db/migrate/20210506064100_add_winner_to_auctify_sales.rb +7 -0
  102. data/db/migrate/20210506072438_add_bids_count_to_auctify_sales.rb +7 -0
  103. data/db/migrate/20210507085845_rename_bids_count_to_applied_bids_count.rb +7 -0
  104. data/db/migrate/20210510063146_add_dont_confirm_bids_to_registrations.rb +7 -0
  105. data/db/migrate/20210511081556_add_index_on_cancelled_on.rb +7 -0
  106. data/db/migrate/20210512162942_add_sold_at_to_auctify_sales.rb +7 -0
  107. data/db/migrate/20210607113440_add_commission_in_percent_to_sales_packs.rb +7 -0
  108. data/db/migrate/20210617062509_add_index_to_auctify_sales_currently_ends_at.rb +7 -0
  109. data/db/migrate/20210625125732_add_current_winner_to_auctify_sales.rb +7 -0
  110. data/lib/auctify.rb +7 -0
  111. data/lib/auctify/configuration.rb +43 -0
  112. data/lib/auctify/engine.rb +25 -0
  113. data/lib/auctify/version.rb +5 -0
  114. data/lib/tasks/auctify_tasks.rake +23 -0
  115. metadata +442 -0
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auctify
4
+ module Behavior
5
+ module Buyer
6
+ extend ActiveSupport::Concern
7
+
8
+ include Auctify::Behavior::Base
9
+
10
+ included do
11
+ has_many :purchases, as: :buyer, class_name: "Auctify::Sale::Base"
12
+ has_many :bidder_registrations, as: :bidder, class_name: "Auctify::BidderRegistration"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auctify
4
+ module Behavior
5
+ module Item
6
+ extend ActiveSupport::Concern
7
+
8
+ include Auctify::Behavior::Base
9
+
10
+ included do
11
+ has_many :sales, class_name: "Auctify::Sale::Base", foreign_key: :item_id, inverse_of: :item
12
+ has_many :auction_sales, class_name: "Auctify::Sale::Auction", foreign_key: :item_id, inverse_of: :item
13
+ has_many :retail_sales, class_name: "Auctify::Sale::Retail", foreign_key: :item_id, inverse_of: :item
14
+
15
+ c_name = self.name
16
+ Auctify::Sale::Base.class_eval do
17
+ belongs_to :item, class_name: c_name, counter_cache: :sales_count
18
+ end
19
+
20
+ has_one :last_published_sale, -> { merge(Auctify::Sale::Base.latest_published_by_item) },
21
+ class_name: "Auctify::Sale::Base",
22
+ foreign_key: :item_id,
23
+ inverse_of: :item
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auctify
4
+ module Behavior
5
+ module Seller
6
+ extend ActiveSupport::Concern
7
+
8
+ include Auctify::Behavior::Base
9
+
10
+ included do
11
+ has_many :sales, as: :seller, class_name: "Auctify::Sale::Base"
12
+ has_many :auction_sales, as: :seller, class_name: "Auctify::Sale::Auction"
13
+ has_many :retail_sales, as: :seller, class_name: "Auctify::Sale::Retail"
14
+
15
+ def offer_to_sale!(item, options = {})
16
+ assoc = options[:in] == :auction ? auction_sales : retail_sales
17
+
18
+ if item.class.included_modules.include?(Auctify::Behavior::Item)
19
+ assoc.create!(item: item,
20
+ seller: self,
21
+ buyer: nil,
22
+ offered_price: options[:price],
23
+ # FIXME @foton - this method will not be used in AA
24
+ ends_at: options[:in] == :auction ? 1.day.from_now : nil)
25
+ else
26
+ sale = assoc.build(seller: self,
27
+ buyer: nil,
28
+ offered_price: options[:price])
29
+ sale.errors.add(:item, :not_auctified)
30
+ raise ActiveRecord::RecordInvalid.new(sale)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auctify
4
+ module Sale
5
+ module AuctionCallbacks
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ CALLBACKS_WITHOUT_ARGS = %i[after_start_sale
10
+ before_bidding_is_close_to_end
11
+ after_close_bidding
12
+ after_sold_in_auction
13
+ after_not_sold_in_auction]
14
+ CALLBACKS_WITH_ARGS = %i[after_bid_appended
15
+ after_bid_not_appended ]
16
+
17
+
18
+ CALLBACKS_WITHOUT_ARGS.each do |cb|
19
+ define_method cb do
20
+ # override me, if You want
21
+ callback_runs[cb] += 1
22
+ end
23
+ end
24
+
25
+ def after_bid_appended(bid_appender)
26
+ # override me, if You want
27
+ # see bid_appender.result
28
+ # to get previous_winning bid, call `previous_winning_bid(bid_appender.bid)`
29
+ callback_runs[:after_bid_appended] += 1
30
+ end
31
+
32
+ def after_bid_not_appended(bid_appender)
33
+ # override me, if You want
34
+ # see bid_appender.errors
35
+ callback_runs[:after_bid_not_appended] += 1
36
+ end
37
+
38
+ def callback_runs
39
+ @callback_runs ||= (CALLBACKS_WITH_ARGS + CALLBACKS_WITHOUT_ARGS).index_with { |cb| 0 }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auctify
4
+ module Api
5
+ module V1
6
+ class AuctionsController < Auctify::Api::V1::BaseController
7
+ before_action :find_auction, except: [:index]
8
+
9
+ def show
10
+ render_record @auction
11
+ end
12
+
13
+ def bids
14
+ if params[:confirmation] == "1"
15
+ if @auction.bid!(new_bid)
16
+ if params[:dont_confirm_bids] == "1"
17
+ # use SQL update in case of some obscure invalid attributes
18
+ current_user.bidder_registrations
19
+ .where(auction: @auction)
20
+ .update_all(dont_confirm_bids: true)
21
+ end
22
+
23
+ render_record @auction.reload, success: true
24
+ else
25
+ render_record @auction, bid: new_bid, status: 400
26
+ end
27
+ else
28
+ new_bid.errors.add(:base, :not_confirmed)
29
+ render_record @auction, bid: new_bid, status: 400
30
+ end
31
+ end
32
+
33
+ private
34
+ def find_auction
35
+ @auction = Auctify::Sale::Auction.find(params[:id])
36
+ end
37
+
38
+ def bid_params
39
+ params.require(:bid).permit(:max_price, :price)
40
+ end
41
+
42
+ def new_bid
43
+ @new_bid ||= Auctify::Bid.new(bid_params.merge(registration_params))
44
+ end
45
+
46
+ def registration_params
47
+ bidder_registration = @auction.bidder_registrations.find_by(bidder: current_user)
48
+ return { registration: bidder_registration } if bidder_registration.present?
49
+
50
+ { bidder: current_user }
51
+ end
52
+
53
+ def render_record(auction, bid: nil, status: 200, success: nil)
54
+ render json: {
55
+ data: cell("#{global_namespace_path}/auctify/auctions/form", auction, bid: bid, success: success).show
56
+ }, status: status
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auctify
4
+ module Api
5
+ module V1
6
+ class BaseController < ApplicationController
7
+ include Folio::ApiControllerBase
8
+
9
+ # TODO handle authorization here if needed
10
+
11
+ private
12
+ def global_namespace_path
13
+ @global_namespace_path ||= global_namespace.underscore
14
+ end
15
+
16
+ def global_namespace
17
+ @global_namespace ||= Rails.application.class.name.deconstantize
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auctify
4
+ module Api
5
+ module V1
6
+ module Console
7
+ class BidsController < Auctify::Api::V1::BaseController # Folio::Console::Api::BaseController
8
+ before_action :api_authenticate_account!
9
+ before_action :find_bid, except: [:index]
10
+
11
+ def destroy
12
+ auction = @bid.auction
13
+
14
+ if @bid.cancel!
15
+ render_list(auction.reload)
16
+ else
17
+ render_invalid(@bid)
18
+ end
19
+ end
20
+
21
+ private
22
+ def find_bid
23
+ @bid = Auctify::Bid.find(params[:id])
24
+ end
25
+
26
+ def api_authenticate_account!
27
+ fail CanCan::AccessDenied if current_account.blank?
28
+ end
29
+
30
+ def render_list(auction)
31
+ render json: { data: cell("folio/console/auctify/auctions/bid_list", auction).show }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auctify
4
+ class ApplicationController < ActionController::Base
5
+ protect_from_forgery with: :exception
6
+ include Auctify::ApplicationHelper
7
+ end
8
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "auctify/application_controller"
4
+
5
+ module Auctify
6
+ class BidderRegistrationsController < ApplicationController
7
+ before_action :set_bidder_registration, only: [:show, :edit, :update, :destroy]
8
+
9
+ # GET /bidder_registrations
10
+ def index
11
+ @bidder_registrations = BidderRegistration.all
12
+ end
13
+
14
+ # GET /bidder_registrations/1
15
+ def show
16
+ end
17
+
18
+ # GET /bidder_registrations/new
19
+ def new
20
+ @bidder_registration = BidderRegistration.new
21
+ end
22
+
23
+ # GET /bidder_registrations/1/edit
24
+ def edit
25
+ end
26
+
27
+ # POST /bidder_registrations
28
+ def create
29
+ @bidder_registration = BidderRegistration.new(bidder_registration_params)
30
+
31
+ if @bidder_registration.save
32
+ redirect_to @bidder_registration, notice: "Bidder registration was successfully created."
33
+ else
34
+ render :new
35
+ end
36
+ end
37
+
38
+ # PATCH/PUT /bidder_registrations/1
39
+ def update
40
+ if @bidder_registration.update(bidder_registration_params)
41
+ redirect_to @bidder_registration, notice: "Bidder registration was successfully updated."
42
+ else
43
+ render :edit
44
+ end
45
+ end
46
+
47
+ # DELETE /bidder_registrations/1
48
+ def destroy
49
+ @bidder_registration.destroy
50
+ redirect_to auctify_bidder_registrations_url, notice: "Bidder registration was successfully destroyed."
51
+ end
52
+
53
+ private
54
+ # Use callbacks to share common setup or constraints between actions.
55
+ def set_bidder_registration
56
+ @bidder_registration = BidderRegistration.find(params[:id])
57
+ end
58
+
59
+ # Only allow a trusted parameter "white list" through.
60
+ def bidder_registration_params
61
+ params.require(:bidder_registration).permit(:bidder_id, :bidder_type, :auction_id, :aasm_state, :submitted_at,
62
+ :handled_at)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "auctify/application_controller"
4
+
5
+ module Auctify
6
+ class BidsController < ApplicationController
7
+ before_action :set_bid, only: [:show, :edit, :update, :destroy]
8
+
9
+ # GET /bids
10
+ def index
11
+ @bids = Bid.all
12
+ end
13
+
14
+ # GET /bids/1
15
+ def show
16
+ end
17
+
18
+ # GET /bids/new
19
+ def new
20
+ @bid = Bid.new
21
+ end
22
+
23
+ # GET /bids/1/edit
24
+ def edit
25
+ end
26
+
27
+ # POST /bids
28
+ def create
29
+ @bid = Bid.new(bid_params)
30
+
31
+ if @bid.save
32
+ redirect_to @bid, notice: "Bid was successfully created."
33
+ else
34
+ render :new
35
+ end
36
+ end
37
+
38
+ # PATCH/PUT /bids/1
39
+ def update
40
+ if @bid.update(bid_params)
41
+ redirect_to @bid, notice: "Bid was successfully updated."
42
+ else
43
+ render :edit
44
+ end
45
+ end
46
+
47
+ # DELETE /bids/1
48
+ def destroy
49
+ @bid.destroy
50
+ redirect_to auctify_bids_url, notice: "Bid was successfully destroyed."
51
+ end
52
+
53
+ private
54
+ # Use callbacks to share common setup or constraints between actions.
55
+ def set_bid
56
+ @bid = Bid.find(params[:id])
57
+ end
58
+
59
+ # Only allow a trusted parameter "white list" through.
60
+ def bid_params
61
+ params.require(:bid).permit(:registration_id, :price, :max_price)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "auctify/application_controller"
4
+
5
+ module Auctify
6
+ class SalesController < ApplicationController
7
+ before_action :set_sale, only: [:show, :edit, :update, :destroy]
8
+
9
+ # GET /sales
10
+ def index
11
+ @sales = scoped_sales.all
12
+ end
13
+
14
+ # GET /sales/1
15
+ def show
16
+ end
17
+
18
+ # GET /sales/new
19
+ def new
20
+ @sale = sale_class.new
21
+ end
22
+
23
+ # GET /sales/1/edit
24
+ def edit
25
+ end
26
+
27
+ # POST /sales
28
+ def create
29
+ @sale = sale_class.new(sale_params)
30
+
31
+ if @sale.save
32
+ redirect_to auctify_sale_path(@sale), notice: "Sale was successfully created."
33
+ else
34
+ render :new
35
+ end
36
+ end
37
+
38
+ # PATCH/PUT /sales/1
39
+ def update
40
+ if @sale.update(sale_params)
41
+ redirect_to auctify_sale_path(@sale), notice: "Sale was successfully updated."
42
+ else
43
+ render :edit
44
+ end
45
+ end
46
+
47
+ # DELETE /sales/1
48
+ def destroy
49
+ @sale.destroy
50
+ redirect_to auctify_sales_url, notice: "Sale was successfully destroyed."
51
+ end
52
+
53
+ private
54
+ def sale_class
55
+ Sale::Base
56
+ end
57
+
58
+ # Use callbacks to share common setup or constraints between actions.
59
+ def set_sale
60
+ @sale = scoped_sales.find(params[:id])
61
+ end
62
+
63
+ # Only allow a trusted parameter "white list" through.
64
+ def sale_params
65
+ params.require(:sale).permit(:seller_auctify_id, :buyer_auctify_id, :item_auctify_id, :published)
66
+ end
67
+
68
+ def scoped_sales
69
+ scope = sale_class
70
+ return scope if params[:list_all].to_s == "1"
71
+
72
+ scope.published.not_sold
73
+ end
74
+ end
75
+ end