diff --git a/app/controllers/admin/invoices_controller.rb b/app/controllers/admin/invoices_controller.rb index 25b52db70..2818a554a 100644 --- a/app/controllers/admin/invoices_controller.rb +++ b/app/controllers/admin/invoices_controller.rb @@ -1,4 +1,4 @@ -# rubocop:disable Metrics/ClassLength +# rubocop:disable Metrics # require 'invoice_already_paid' module Admin @@ -15,23 +15,16 @@ def show # GET /admin/invoices def index - sort_column = params[:sort].presence_in(%w[paid_through - paid_amount - vat_rate - cents - notes - status - number - due_date - billing_profile_id]) || 'id' - sort_direction = params[:direction].presence_in(%w[asc desc]) || 'desc' - invoices = Invoice.accessible_by(current_ability) .includes(:paid_with_payment_order) .search(params) - .order("#{sort_column} #{sort_direction}") - @pagy, @invoices = pagy(invoices, items: params[:per_page] ||= 15) + if invoices.is_a?(Array) + @pagy, @invoices = pagy_array(invoices, items: params[:per_page] ||= 15) + else + @pagy, @invoices = pagy(invoices, items: params[:per_page] ||= 15) + end + end # GET /admin/invoices/aa450f1a-45e2-4f22-b2c3-f5f46b5f906b/download diff --git a/app/controllers/auctions_controller.rb b/app/controllers/auctions_controller.rb index b6a78541e..c5d564795 100644 --- a/app/controllers/auctions_controller.rb +++ b/app/controllers/auctions_controller.rb @@ -30,9 +30,9 @@ def show def fetch_auctions_list if should_sort_auctions? - Auction.active.ai_score_order.search(params).with_user_offers(current_user&.id) + Auction.active.ai_score_order.search(params, current_user).with_user_offers(current_user&.id) else - Auction.active.search(params).with_user_offers(current_user&.id) + Auction.active.search(params, current_user).with_user_offers(current_user&.id) end end diff --git a/app/controllers/histories_controller.rb b/app/controllers/histories_controller.rb index c77b6ed5e..02ee12d37 100644 --- a/app/controllers/histories_controller.rb +++ b/app/controllers/histories_controller.rb @@ -3,7 +3,6 @@ class HistoriesController < ApplicationController def index auctions = Auction.where('ends_at <= ?', Time.zone.now) - .order(ends_at: :desc, domain_name: :asc) .search(params) @pagy, @auctions = pagy(auctions, items: params[:per_page] ||= 20, diff --git a/app/models/auction.rb b/app/models/auction.rb index 66e33e96d..b9cbd08fc 100644 --- a/app/models/auction.rb +++ b/app/models/auction.rb @@ -1,8 +1,11 @@ +# rubocop:disable Metrics class Auction < ApplicationRecord include Presentable + include Searchable include PgSearch::Model BLIND = '0'.freeze + ENGLISH = '1'.freeze after_create :find_auction_turns validates :domain_name, presence: true @@ -25,61 +28,6 @@ class Auction < ApplicationRecord after_update_commit :update_list_broadcast, unless: :skip_broadcast after_update_commit :update_offer_broadcast, unless: :skip_broadcast - scope :active, -> { where('starts_at <= ? AND ends_at >= ?', Time.now.utc, Time.now.utc) } - scope :without_result, lambda { - where('ends_at < ? and id NOT IN (SELECT results.auction_id FROM results)', Time.now.utc) - } - - scope :for_period, lambda { |start_date, end_date| - where(ends_at: start_date.beginning_of_day..end_date.end_of_day) - } - - scope :random_order, -> { order(Arel.sql('RANDOM()')) } - scope :ai_score_order, lambda { - order(Arel.sql('CASE WHEN ai_score > 0 THEN ai_score ELSE RANDOM() END DESC')) - } - - scope :without_offers, -> { includes(:offers).where(offers: { auction_id: nil }) } - scope :with_offers, -> { includes(:offers).where.not(offers: { auction_id: nil }) } - scope :with_domain_name, (lambda do |domain_name| - return unless domain_name.present? - - where('domain_name like ?', "%#{domain_name}%") - end) - - scope :with_type, (lambda do |type| - if type.present? - return where(platform: [type, nil]) if type == BLIND - - where(platform: type) - end - end) - - scope :with_starts_at, (lambda do |starts_at| - where('starts_at >= ?', starts_at.to_date.beginning_of_day) if starts_at.present? - end) - - scope :with_ends_at, (lambda do |ends_at| - where('ends_at <= ?', ends_at.to_date.end_of_day) if ends_at.present? - end) - scope :with_starts_at_nil, ->(state) { where(starts_at: nil) if state.present? } - - scope :english, -> { where(platform: :english) } - scope :not_english, -> { where.not(platform: :english) } - - scope :with_offers, (lambda do |auction_offer_type, type| - return if auction_offer_type.blank? || type == BLIND || type.empty? - - case auction_offer_type - when 'with_offers' - auction_id_list = self.select { |a| a.offers.present? }.pluck(:id) - when 'without_offers' - auction_id_list = self.select { |a| a.offers.empty? }.pluck(:id) - end - - where(id: auction_id_list) - end) - delegate :count, to: :offers, prefix: true delegate :size, to: :offers, prefix: true @@ -101,32 +49,6 @@ def deposit=(value) self.requirement_deposit_in_cents = deposit.cents end - def self.search(params = {}) - param_list = %w[domain_name starts_at ends_at platform users_price] - sort_column = params[:sort].presence_in(param_list) || 'domain_name' - sort_admin_column = params[:sort].presence_in(%w[domain name - starts_at - ends_at - highest_offer_cents - number_of_offers - turns_count - starting_price - min_bids_step - slipping_end - platform]) || 'id' - sort_direction = params[:direction].presence_in(%w[asc desc]) || 'desc' - is_from_admin = params[:admin] == 'true' - - with_highest_offers - .with_domain_name(params[:domain_name]) - .with_type(params[:type]) - .with_starts_at(params[:starts_at]) - .with_ends_at(params[:ends_at]) - .with_starts_at_nil(params[:starts_at_nil]) - .with_offers(params[:auction_offer_type], params[:type]) - .order("#{is_from_admin ? sort_admin_column : sort_column} #{sort_direction} NULLS LAST") - end - def deposit_and_enable_deposit_should_be_togeter return unless english? return if (requirement_deposit_in_cents.nil? || requirement_deposit_in_cents.zero?) && !enable_deposit diff --git a/app/models/auction/searchable.rb b/app/models/auction/searchable.rb new file mode 100644 index 000000000..aebd19c58 --- /dev/null +++ b/app/models/auction/searchable.rb @@ -0,0 +1,143 @@ +# rubocop:disable Metrics +module Auction::Searchable + extend ActiveSupport::Concern + + included do + scope :active, -> { where('starts_at <= ? AND ends_at >= ?', Time.now.utc, Time.now.utc) } + scope :without_result, lambda { + where('ends_at < ? and id NOT IN (SELECT results.auction_id FROM results)', Time.now.utc) + } + + scope :for_period, lambda { |start_date, end_date| + where(ends_at: start_date.beginning_of_day..end_date.end_of_day) + } + + scope :random_order, -> { order(Arel.sql('RANDOM()')) } + scope :ai_score_order, lambda { + order(Arel.sql('CASE WHEN ai_score > 0 THEN ai_score ELSE RANDOM() END DESC')) + } + + scope :without_offers, -> { includes(:offers).where(offers: { auction_id: nil }) } + scope :with_offers, -> { includes(:offers).where.not(offers: { auction_id: nil }) } + scope :with_domain_name, (lambda do |domain_name| + return unless domain_name.present? + + where('domain_name like ?', "%#{domain_name}%") + end) + + scope :with_type, (lambda do |type| + if type.present? + return where(platform: [type, nil]) if type == BLIND + + where(platform: type) + end + end) + + scope :with_starts_at, (lambda do |starts_at| + where('starts_at >= ?', starts_at.to_date.beginning_of_day) if starts_at.present? + end) + + scope :with_ends_at, (lambda do |ends_at| + where('ends_at <= ?', ends_at.to_date.end_of_day) if ends_at.present? + end) + scope :with_starts_at_nil, ->(state) { where(starts_at: nil) if state.present? } + + scope :english, -> { where(platform: :english) } + scope :not_english, -> { where.not(platform: :english) } + + scope :with_offers, (lambda do |auction_offer_type, type| + return if auction_offer_type.blank? || type == BLIND || type.empty? + + case auction_offer_type + when 'with_offers' + auction_id_list = self.select { |a| a.offers.present? }.pluck(:id) + when 'without_offers' + auction_id_list = self.select { |a| a.offers.empty? }.pluck(:id) + end + + where(id: auction_id_list) + end) + end + + class_methods do + def search(params = {}, current_user = nil) + param_list = %w[domain_name starts_at ends_at platform users_price] + sort_column = params[:sort].presence_in(param_list) || 'domain_name' + sort_admin_column = params[:sort].presence_in(%w[domain_name + starts_at + ends_at + highest_offer_cents + number_of_offers + turns_count + starting_price + min_bids_step + slipping_end + platform + requirement_deposit_in_cents + enable_deposit]) || 'id' + sort_direction = params[:direction].presence_in(%w[asc desc]) || 'desc' + is_from_admin = params[:admin] == 'true' + + query = + with_highest_offers + .with_domain_name(params[:domain_name]) + .with_type(params[:type]) + .with_starts_at(params[:starts_at]) + .with_ends_at(params[:ends_at]) + .with_starts_at_nil(params[:starts_at_nil]) + .with_offers(params[:auction_offer_type], params[:type]) + + if params[:sort] == 'users_price' + query.with_max_offer_cents_for_english_auction(current_user).order("offers_subquery.max_offer_cents #{sort_direction} NULLS LAST") + elsif params[:sort] == 'username' + query.sorted_by_winning_offer_username.order("offers_subquery.username #{sort_direction} NULLS LAST") + else + query.order("#{is_from_admin ? sort_admin_column : sort_column} #{sort_direction} NULLS LAST") + end + end + + def with_max_offer_cents_for_english_auction(user = nil) + if user + joins(<<-SQL + LEFT JOIN ( + SELECT auction_id, MAX(cents) AS max_offer_cents + FROM offers + WHERE auction_id IN ( + SELECT id FROM auctions WHERE platform = 1 + UNION + SELECT auction_id FROM offers WHERE user_id = #{user.id} AND auction_id IN (SELECT id FROM auctions WHERE platform IS NULL OR platform = 0) + ) + GROUP BY auction_id + ) AS offers_subquery ON auctions.id = offers_subquery.auction_id + SQL + ) + else + joins(<<-SQL + LEFT JOIN ( + SELECT auction_id, MAX(cents) AS max_offer_cents + FROM offers + WHERE auction_id IN (SELECT id FROM auctions WHERE platform = 1) + GROUP BY auction_id + ) AS offers_subquery ON auctions.id = offers_subquery.auction_id + SQL + ) + end + end + + def sorted_by_winning_offer_username + joins(<<-SQL + LEFT JOIN ( + SELECT offers.auction_id, offers.username + FROM offers + WHERE offers.cents = ( + SELECT MAX(offers_inner.cents) + FROM offers AS offers_inner + WHERE offers_inner.auction_id = offers.auction_id + ) + GROUP BY offers.auction_id, offers.username + ) AS offers_subquery ON auctions.id = offers_subquery.auction_id + SQL + ) + end + end +end diff --git a/app/models/invoice.rb b/app/models/invoice.rb index fa66fef3b..bdac64f4b 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -1,7 +1,4 @@ -# rubocop:disable Metrics/ClassLength -# require 'result_not_found' -# require 'result_not_sold' -# require 'countries' +# rubocop:disable Metrics class Invoice < ApplicationRecord include BookKeeping @@ -33,6 +30,8 @@ class Invoice < ApplicationRecord delegate :enable_deposit?, to: :enable_deposit? delegate :deposit, to: :deposit + attr_accessor :vat_rate + scope :with_search_scope, (lambda do |origin| if origin.present? if numeric?(origin) @@ -44,10 +43,10 @@ class Invoice < ApplicationRecord .where('billing_profiles.name ILIKE ? OR ' \ 'users.email ILIKE ? OR users.surname ILIKE ? OR ' \ 'invoice_items.name ILIKE ?', - "%#{origin}%", - "%#{origin}%", - "%#{origin}%", - "%#{origin}%") + "%#{origin}%", + "%#{origin}%", + "%#{origin}%", + "%#{origin}%") end end end) @@ -65,7 +64,55 @@ class Invoice < ApplicationRecord } def self.search(params = {}) - with_search_scope(params[:search_string]).with_statuses(params[:statuses_contains]) + sort_column = params[:sort].presence_in(%w[paid_through + paid_amount + vat_rate + cents + notes + status + number + due_date + billing_profile_name]) || 'id' + sort_direction = params[:direction].presence_in(%w[asc desc]) || 'desc' + + query = with_search_scope(params[:search_string]).with_statuses(params[:statuses_contains]) + + case params[:sort] + when 'channel' + # invoices_array = query.to_a + + # if sort_direction == 'asc' + # invoices_array.sort_by do |invoice| + # invoice.paid_with_payment_order&.channel || '' + # end + # else + # invoices_array.sort_by do |invoice| + # invoice.paid_with_payment_order&.channel || '' + # end.reverse + # end + + query.left_outer_joins(:paid_with_payment_order) + .select("invoices.*, REPLACE(payment_orders.type, 'PaymentOrders::', '') AS payment_order_channel") + .order(Arel.sql("payment_order_channel #{sort_direction} NULLS LAST")) + + # query.left_outer_joins(:paid_with_payment_order) + # .select("invoices.*, COALESCE(REPLACE(paid_with_payment_orders.type, 'PaymentOrders::', ''), '') AS payment_order_channel") + # .order(Arel.sql("payment_order_channel #{sort_direction} NULLS LAST")) + + + when 'billing_profile_name' + query.left_outer_joins(:billing_profile).order("billing_profiles.name #{sort_direction}") + when 'total' + invoices_array = query.to_a + + if sort_direction == 'asc' + invoices_array.sort_by(&:total) + else + invoices_array.sort_by(&:total).reverse + end + else + query.order("#{sort_column} #{sort_direction} NULLS LAST") + end end def self.create_from_result(result_id) @@ -148,7 +195,7 @@ def vat end def title - persisted? ? I18n.t('invoices.title', number: number) : nil + persisted? ? I18n.t('invoices.title', number:) : nil end def address @@ -219,7 +266,7 @@ def update_billing_address end def self.with_billing_profile(billing_profile_id:) - Invoice.where(billing_profile_id: billing_profile_id) + Invoice.where(billing_profile_id:) end def self.numeric?(string) diff --git a/app/models/payment_orders/lhv.rb b/app/models/payment_orders/lhv.rb index 7c1fa04d9..3352ab083 100644 --- a/app/models/payment_orders/lhv.rb +++ b/app/models/payment_orders/lhv.rb @@ -1,5 +1,5 @@ module PaymentOrders - class Lhv < EstonianBankLink + class LHV < EstonianBankLink def self.config_namespace_name 'lhv' end diff --git a/app/models/payment_orders/seb.rb b/app/models/payment_orders/seb.rb index 2e05c8838..62d63d21e 100644 --- a/app/models/payment_orders/seb.rb +++ b/app/models/payment_orders/seb.rb @@ -1,5 +1,5 @@ module PaymentOrders - class Seb < EstonianBankLink + class SEB < EstonianBankLink def self.config_namespace_name 'seb' end diff --git a/app/views/admin/invoices/index.html.erb b/app/views/admin/invoices/index.html.erb index cf7388c3a..a1ebe0821 100644 --- a/app/views/admin/invoices/index.html.erb +++ b/app/views/admin/invoices/index.html.erb @@ -40,8 +40,7 @@ <%= t('invoices.billing_profile') %> - <%#= order_buttons('invoices.billing_profile_id') %> - <%= sort_link_to "", "billing_profile_id" %> + <%= sort_link_to "", "billing_profile_name" %> <%= t('invoices.due_date') %> @@ -61,7 +60,7 @@ <%= t('invoices.total') %> - <%= sort_link_to "", "cents" %> + <%= sort_link_to "", "total" %> <%= t('invoices.vat_rate_on_payment') %> @@ -73,7 +72,7 @@ <%= t('invoices.paid_through') %> - <%= sort_link_to "", "paid_through" %> + <%= sort_link_to "", "channel" %> diff --git a/app/views/auctions/_auction.html.erb b/app/views/auctions/_auction.html.erb index 44e112854..da47b39a7 100644 --- a/app/views/auctions/_auction.html.erb +++ b/app/views/auctions/_auction.html.erb @@ -15,10 +15,10 @@ <% if auction.platform == 'blind' || auction.platform.nil? %> - <%= t('auctions.your_current_price') %>: <%= auction.users_price %> + <%= t('auctions.your_current_price') %>: <%= number_with_precision(auction.users_price, precision: 2, separator: ",") %> <% else %> <%= t('auctions.your_current_price') %>: - <%= english_auction_presenter.maximum_bids %> + <%= number_with_precision(english_auction_presenter.maximum_bids, precision: 2, separator: ",") %> <% end %> diff --git a/app/views/auctions/index.html.erb b/app/views/auctions/index.html.erb index 74091d421..30c77968f 100644 --- a/app/views/auctions/index.html.erb +++ b/app/views/auctions/index.html.erb @@ -75,7 +75,7 @@
- <%= link_to t('auctions.finished_auctions'), histories_path, style: 'margin-bottom: 20px; display: block; font-size: 18px; color: #03456f;' %> + <%= link_to t('auctions.finished_auctions'), "#{histories_path}?direction=desc&sort=domain_name", style: 'margin-bottom: 20px; display: block; font-size: 18px; color: #03456f;' %>
@@ -105,13 +105,13 @@ <%= t('auctions.current_price') %> - <%= sort_link_to "", "users_offer_cents" %> + <%= sort_link_to "", "users_price" %> <%= t('auctions.offer_owner') %> - <%= sort_link_to "", "platform" %> + <%= sort_link_to "", "username" %> <%= t('auctions.offer_actions') %> diff --git a/app/views/histories/index.html.erb b/app/views/histories/index.html.erb index 3324bbaf9..1f78bd954 100644 --- a/app/views/histories/index.html.erb +++ b/app/views/histories/index.html.erb @@ -42,7 +42,7 @@
- <%= link_to t('histories.current_auctions'), auctions_path, style: 'margin-bottom: 20px; display: block; font-size: 18px; color: #03456f;' %> + <%= link_to t('histories.current_auctions'), auctions_path, style: 'margin-bottom: 20px; display: block; font-size: 18px; color: #03456f;', target: '_top' %>
@@ -63,6 +63,7 @@ <%= t('auctions.winning_bid') %> + <%= sort_link_to "", "users_price" %> <%= t('auctions.auction_type') %> @@ -79,7 +80,7 @@ <%= auction.ends_at.to_formatted_s(:long) %> <%= auction.currently_winning_offer&.price if auction.platform == 'english' %> - <%= t("auctions.#{auction.platform&.downcase.presence || 'blind'}") %> + <%= t("auctions.#{auction.platform&.downcase.presence || 'blind (lagacy)'}") %> <% end %> diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index ac033bf9d..ad336e3c4 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -14,3 +14,8 @@ # ActiveSupport::Inflector.inflections(:en) do |inflect| # inflect.acronym 'RESTful' # end + +ActiveSupport::Inflector.inflections(:en) do |inflect| + inflect.acronym 'LHV' + inflect.acronym 'SEB' +end diff --git a/config/initializers/pagy.rb b/config/initializers/pagy.rb index 61d115eba..dc2790f7e 100644 --- a/config/initializers/pagy.rb +++ b/config/initializers/pagy.rb @@ -1,4 +1,6 @@ require 'pagy/extras/overflow' +require "pagy/extras/array" + Pagy::I18n.load({ locale: 'en', filepath: "#{Rails.root}/config/locales/pagy.en.yml" }, { locale: 'et',