diff --git a/app/controllers/country_bands_controller.rb b/app/controllers/country_bands_controller.rb index 1ba7f09fc3..5307169b01 100644 --- a/app/controllers/country_bands_controller.rb +++ b/app/controllers/country_bands_controller.rb @@ -10,7 +10,7 @@ def index def edit @number = id_from_params - unless CountryBand::BANDS.keys.include?(@number) + unless CountryBandDetail.exists?(number: @number) flash[:danger] = "Unknown band number" return redirect_to country_bands_path end diff --git a/app/models/country_band.rb b/app/models/country_band.rb index 1e713a972a..43d10915a9 100644 --- a/app/models/country_band.rb +++ b/app/models/country_band.rb @@ -1,46 +1,9 @@ # frozen_string_literal: true class CountryBand < ApplicationRecord - BANDS = { - 0 => { - value: 0.00, - }, - 1 => { - value: 0.19, - }, - 2 => { - value: 0.32, - }, - 3 => { - value: 0.45, - }, - 4 => { - value: 2.28, - }, - 5 => { - value: 3.00, - }, - }.freeze - - # According to WCA's current dues policy, the due amount per competitor is equivalent - # to this percent of registration fee. Only used if this due amount per competitor is - # larger than the due amount per competitor calculated from the competition's country band. - PERCENT_REGISTRATION_FEE_USED_FOR_DUE_AMOUNT = 0.15 - - def self.percent_registration_fee_used_for_due_amount(country_band) - return 0 if country_band.nil? - if country_band >= 3 - 0.15 - elsif country_band >= 1 - 0.05 - else - 0.00 - end - end - belongs_to :country, foreign_key: :iso2, primary_key: :iso2 + belongs_to :country_band_detail, foreign_key: :number, primary_key: :number validates_inclusion_of :iso2, in: Country::WCA_COUNTRY_ISO_CODES - validates_inclusion_of :number, in: BANDS.keys.freeze def country Country.find_by_iso2(self.iso2) diff --git a/app/models/country_band_detail.rb b/app/models/country_band_detail.rb new file mode 100644 index 0000000000..efd46e25a2 --- /dev/null +++ b/app/models/country_band_detail.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class CountryBandDetail < ApplicationRecord + scope :active, -> { where(end_date: nil).or(inactive.invert_where) } + scope :inactive, -> { where(end_date: ..Date.today) } +end diff --git a/app/views/country_bands/_show_band.html.erb b/app/views/country_bands/_show_band.html.erb index ec25489639..ca800f0da2 100644 --- a/app/views/country_bands/_show_band.html.erb +++ b/app/views/country_bands/_show_band.html.erb @@ -1,12 +1,12 @@
- <%= t("country_bands.band_title", number: number) %> - (<%= t("country_bands.due_value", value: data[:value]) %>) + <%= t("country_bands.band_title", number: country_band_detail.number) %> + (<%= t("country_bands.due_value", value: country_band_detail.due_amount_per_competitor_in_cents.to_f / 100) %>) <% if current_user&.can_admin_finances? %> - <%= link_to(ui_icon("pencil alt"), edit_country_band_path(id: number)) %> + <%= link_to(ui_icon("pencil alt"), edit_country_band_path(id: country_band_detail.number)) %> <% end %>
- <% (@country_bands_by_number[number] || []).map(&:country).sort_by(&:name).each do |c| %> + <% (@country_bands_by_number[country_band_detail.number] || []).map(&:country).sort_by(&:name).each do |c| %>
<%= flag_icon c.iso2 %>
diff --git a/app/views/country_bands/index.html.erb b/app/views/country_bands/index.html.erb index 395439736a..3bb3eeb37d 100644 --- a/app/views/country_bands/index.html.erb +++ b/app/views/country_bands/index.html.erb @@ -14,8 +14,8 @@
- <% CountryBand::BANDS.each do |number, data| %> - <%= render "show_band", number: number, data: data %> + <% CountryBandDetail.active.order(:number).each do |country_band_detail| %> + <%= render "show_band", country_band_detail: country_band_detail %> <% end %>
diff --git a/db/migrate/20241225030959_create_country_band_details.rb b/db/migrate/20241225030959_create_country_band_details.rb new file mode 100644 index 0000000000..ad16e10257 --- /dev/null +++ b/db/migrate/20241225030959_create_country_band_details.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateCountryBandDetails < ActiveRecord::Migration[7.2] + def change + create_table :country_band_details do |t| + t.integer "number", null: false + t.date "start_date", null: false + t.date "end_date" + t.integer "due_amount_per_competitor_in_cents", null: false + t.integer "due_percent_registration_fee", null: false + t.timestamps + end + end +end diff --git a/db/migrate/20241225031242_populate_country_band_details.rb b/db/migrate/20241225031242_populate_country_band_details.rb new file mode 100644 index 0000000000..9aec86245a --- /dev/null +++ b/db/migrate/20241225031242_populate_country_band_details.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +class PopulateCountryBandDetails < ActiveRecord::Migration[7.2] + def up + CountryBandDetail.create!( + number: 0, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 0, + due_percent_registration_fee: 0, + ) + CountryBandDetail.create!( + number: 1, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 19, + due_percent_registration_fee: 5, + ) + CountryBandDetail.create!( + number: 2, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 32, + due_percent_registration_fee: 5, + ) + CountryBandDetail.create!( + number: 3, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 45, + due_percent_registration_fee: 15, + ) + CountryBandDetail.create!( + number: 4, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 228, + due_percent_registration_fee: 15, + ) + CountryBandDetail.create!( + number: 5, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 300, + due_percent_registration_fee: 15, + ) + end + + def down + CountryBandDetail.delete_all + end +end diff --git a/db/schema.rb b/db/schema.rb index 76cf2d624c..cde87543bf 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_11_24_050607) do +ActiveRecord::Schema[7.2].define(version: 2024_12_25_031242) do create_table "Competitions", id: { type: :string, limit: 32, default: "" }, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "name", limit: 50, default: "", null: false t.string "cityName", limit: 50, default: "", null: false @@ -665,6 +665,16 @@ t.datetime "updated_at", null: false end + create_table "country_band_details", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.integer "number", null: false + t.date "start_date", null: false + t.date "end_date" + t.integer "due_amount_per_competitor_in_cents", null: false + t.integer "due_percent_registration_fee", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "country_bands", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.integer "number", null: false t.string "iso2", limit: 2, null: false diff --git a/db/seeds/country_band_details.seeds.rb b/db/seeds/country_band_details.seeds.rb new file mode 100644 index 0000000000..b36bc9813b --- /dev/null +++ b/db/seeds/country_band_details.seeds.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +CountryBandDetail.create!( + number: 0, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 0, + due_percent_registration_fee: 0, +) +CountryBandDetail.create!( + number: 1, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 19, + due_percent_registration_fee: 5, +) +CountryBandDetail.create!( + number: 2, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 32, + due_percent_registration_fee: 5, +) +CountryBandDetail.create!( + number: 3, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 45, + due_percent_registration_fee: 15, +) +CountryBandDetail.create!( + number: 4, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 228, + due_percent_registration_fee: 15, +) +CountryBandDetail.create!( + number: 5, + start_date: '2018-01-01', + due_amount_per_competitor_in_cents: 300, + due_percent_registration_fee: 15, +) diff --git a/lib/database_dumper.rb b/lib/database_dumper.rb index 644cb64f34..17b59c5961 100644 --- a/lib/database_dumper.rb +++ b/lib/database_dumper.rb @@ -855,6 +855,20 @@ def self.actions_to_column_sanitizers(columns_by_action) ), ), }.freeze, + "country_band_details" => { + column_sanitizers: actions_to_column_sanitizers( + copy: %w( + id + number + start_date + end_date + due_amount_per_competitor_in_cents + due_percent_registration_fee + created_at + updated_at + ), + ), + }.freeze, "user_roles" => { where_clause: "JOIN user_groups ON user_groups.id=group_id WHERE NOT user_groups.is_hidden", column_sanitizers: actions_to_column_sanitizers( diff --git a/lib/dues_calculator.rb b/lib/dues_calculator.rb index 536d807c6f..ce09592f2c 100644 --- a/lib/dues_calculator.rb +++ b/lib/dues_calculator.rb @@ -20,16 +20,19 @@ def self.dues_for_n_competitors(country_iso2, base_entry_fee_lowest_denomination def self.dues_per_competitor_in_usd(country_iso2, base_entry_fee_lowest_denomination, currency_code) country_band = CountryBand.find_by(iso2: country_iso2)&.number + country_band_detail = CountryBandDetail.find_by(number: country_band) + registration_fees = Money.new(base_entry_fee_lowest_denomination, currency_code).exchange_to("USD") DuesCalculator.update_exchange_rates_if_needed - input_money_us_dollars = Money.new(base_entry_fee_lowest_denomination, currency_code).exchange_to("USD") - registration_fee_dues_us_dollars = input_money_us_dollars * CountryBand.percent_registration_fee_used_for_due_amount(country_band) - country_band_dues_us_dollars = country_band.present? && country_band > 0 ? CountryBand::BANDS[country_band][:value] : 0 - # times 100 because Money require lowest currency subunit, which is cents for USD - country_band_dues_us_dollars_money = Money.new(country_band_dues_us_dollars * 100, "USD") + # Calculation of 'registration fee dues' + registration_fee_dues = Money.new(registration_fees * (country_band_detail&.due_percent_registration_fee.to_f || 0) / 100, "USD") - [registration_fee_dues_us_dollars, country_band_dues_us_dollars_money].max + # Calculation of 'country band dues' + country_band_dues = Money.new(country_band_detail&.due_amount_per_competitor_in_cents || 0, "USD") + + # The maximum of the two is the total dues per competitor + [registration_fee_dues, country_band_dues].max rescue Money::Currency::UnknownCurrency, CurrencyUnavailable nil end diff --git a/spec/models/country_band_spec.rb b/spec/models/country_band_spec.rb index 94507e454d..882a162874 100644 --- a/spec/models/country_band_spec.rb +++ b/spec/models/country_band_spec.rb @@ -15,6 +15,6 @@ it "invalidates band with invalid band id" do cb = CountryBand.new(number: 6, iso2: "HELLO") - expect(cb).to be_invalid_with_errors(number: ["is not included in the list"]) + expect(cb).to be_invalid_with_errors(country: ["must exist"]) end end diff --git a/spec/support/test_db_manager.rb b/spec/support/test_db_manager.rb index 924cf1dacf..5bcb895c23 100644 --- a/spec/support/test_db_manager.rb +++ b/spec/support/test_db_manager.rb @@ -16,6 +16,7 @@ class TestDbManager groups_metadata_councils groups_metadata_teams_committees groups_metadata_translators + country_band_details ).freeze def self.fill_tables