From 730072e91e886caaa0e346d8bea88ec883dba143 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Wed, 21 Mar 2018 16:44:26 +0100 Subject: [PATCH 1/6] Add max votable headings to groups --- .../admin/budget_groups_controller.rb | 2 +- app/views/admin/budgets/_group_form.html.erb | 13 ++++++ ...d_max_votable_headings_to_budget_groups.rb | 5 +++ db/schema.rb | 5 ++- spec/features/admin/budget_groups_spec.rb | 45 +++++++++++++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20180320104823_add_max_votable_headings_to_budget_groups.rb diff --git a/app/controllers/admin/budget_groups_controller.rb b/app/controllers/admin/budget_groups_controller.rb index 512e2bfcd..d8048de48 100644 --- a/app/controllers/admin/budget_groups_controller.rb +++ b/app/controllers/admin/budget_groups_controller.rb @@ -17,7 +17,7 @@ class Admin::BudgetGroupsController < Admin::BaseController private def budget_group_params - params.require(:budget_group).permit(:name) + params.require(:budget_group).permit(:name, :max_votable_headings) end end diff --git a/app/views/admin/budgets/_group_form.html.erb b/app/views/admin/budgets/_group_form.html.erb index c200973f0..0bb83b1b7 100644 --- a/app/views/admin/budgets/_group_form.html.erb +++ b/app/views/admin/budgets/_group_form.html.erb @@ -8,6 +8,19 @@ maxlength: 50, placeholder: t("admin.budgets.form.group"), class: "input-group-field" %> + + <% if group.persisted? %> +
+ <%= f.label :name, t("admin.budgets.form.max_votable_headings") %> + + <%= f.select :max_votable_headings, + (1..group.headings.count), + label: false, + placeholder: t("admin.budgets.form.max_votable_headings"), + class: "input-group-field" %> +
+ <% end %> +
<%= f.submit button_title, class: "button success" %>
diff --git a/db/migrate/20180320104823_add_max_votable_headings_to_budget_groups.rb b/db/migrate/20180320104823_add_max_votable_headings_to_budget_groups.rb new file mode 100644 index 000000000..e030b7b13 --- /dev/null +++ b/db/migrate/20180320104823_add_max_votable_headings_to_budget_groups.rb @@ -0,0 +1,5 @@ +class AddMaxVotableHeadingsToBudgetGroups < ActiveRecord::Migration + def change + add_column :budget_groups, :max_votable_headings, :integer, default: 1 + end +end diff --git a/db/schema.rb b/db/schema.rb index a32417cc1..945f0d52e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180220211105) do +ActiveRecord::Schema.define(version: 20180320104823) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -101,8 +101,9 @@ ActiveRecord::Schema.define(version: 20180220211105) do create_table "budget_groups", force: :cascade do |t| t.integer "budget_id" - t.string "name", limit: 50 + t.string "name", limit: 50 t.string "slug" + t.integer "max_votable_headings", default: 1 end add_index "budget_groups", ["budget_id"], name: "index_budget_groups_on_budget_id", using: :btree diff --git a/spec/features/admin/budget_groups_spec.rb b/spec/features/admin/budget_groups_spec.rb index 90eaa90ac..d7c03d2e3 100644 --- a/spec/features/admin/budget_groups_spec.rb +++ b/spec/features/admin/budget_groups_spec.rb @@ -65,4 +65,49 @@ feature 'Admin can change the groups name' do expect(page).to have_content('has already been taken') end + context "Maximum votable headings" do + + background do + 3.times { create(:budget_heading, group: group) } + end + + scenario "Defaults to 1 heading per group", :js do + visit admin_budget_path(group.budget) + + within("#budget_group_#{group.id}") do + click_link 'Edit group' + + expect(page).to have_select('budget_group_max_votable_headings', selected: '1') + end + end + + scenario "Update", :js do + visit admin_budget_path(group.budget) + + within("#budget_group_#{group.id}") do + click_link 'Edit group' + + select '2', from: 'budget_group_max_votable_headings' + click_button 'Save group' + end + + visit admin_budget_path(group.budget) + + within("#budget_group_#{group.id}") do + click_link 'Edit group' + + expect(page).to have_select('budget_group_max_votable_headings', selected: '2') + end + end + + scenario "Do not display maxium votable headings' select in new form", :js do + visit admin_budget_path(group.budget) + + click_link 'Add new group' + + expect(page).to have_field('budget_group_name') + expect(page).to_not have_field('budget_group_max_votable_headings') + end + + end end From bdffb9765e8079c86fc16cda312b201f2ad3c50a Mon Sep 17 00:00:00 2001 From: rgarcia Date: Wed, 21 Mar 2018 19:43:40 +0100 Subject: [PATCH 2/6] Allow voting in multiple headings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have the option of voting in multiple headings per group, the method of voting in a “different heading assigned” has become deprecated and thus removed --- app/models/budget/investment.rb | 8 +-- app/views/budgets/investments/_votes.html.erb | 3 +- config/locales/en/admin.yml | 1 + config/locales/en/budgets.yml | 4 +- config/locales/es/admin.yml | 1 + config/locales/es/budgets.yml | 4 +- spec/features/budgets/votes_spec.rb | 69 +++++++++++++++++++ spec/models/budget/investment_spec.rb | 43 ++++++++++++ 8 files changed, 126 insertions(+), 7 deletions(-) diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index dff2dd2e0..28cc5bded 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -231,12 +231,12 @@ class Budget end def valid_heading?(user) - !different_heading_assigned?(user) + voted_in?([heading.id], user) || + can_vote_in_another_heading?(user) end - def different_heading_assigned?(user) - other_heading_ids = group.heading_ids - [heading.id] - voted_in?(other_heading_ids, user) + def can_vote_in_another_heading?(user) + headings_voted_by_user(user).count < group.max_votable_headings end def voted_in?(heading_ids, user) diff --git a/app/views/budgets/investments/_votes.html.erb b/app/views/budgets/investments/_votes.html.erb index 035ff5edb..421905038 100644 --- a/app/views/budgets/investments/_votes.html.erb +++ b/app/views/budgets/investments/_votes.html.erb @@ -19,7 +19,7 @@ title: t('budgets.investments.investment.support_title'), method: "post", remote: (current_user && current_user.voted_in_group?(investment.group) ? true : false), - data: (current_user && current_user.voted_in_group?(investment.group) ? nil : { confirm: t('budgets.investments.investment.confirm_group')} ), + data: (current_user && current_user.voted_in_group?(investment.group) ? nil : { confirm: t('budgets.investments.investment.confirm_group', count: investment.group.max_votable_headings)} ), "aria-hidden" => css_for_aria_hidden(reason) do %> <%= t("budgets.investments.investment.give_support") %> <% end %> @@ -31,6 +31,7 @@

<%= t("votes.budget_investments.#{reason}", + count: investment.group.max_votable_headings, verify_account: link_to(t("votes.verify_account"), verification_path), signin: link_to(t("votes.signin"), new_user_session_path), signup: link_to(t("votes.signup"), new_user_registration_path) diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index baa678996..ad03c3a13 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -119,6 +119,7 @@ en: table_amount: Amount table_population: Population population_info: "Budget Heading population field is used for Statistic purposes at the end of the Budget to show for each Heading that represents an area with population what percentage voted. The field is optional so you can leave it empty if it doesn't apply." + max_votable_headings: "Maxium number of headings in which a user can vote" winners: calculate: Calculate Winner Investments calculated: Winners being calculated, it may take a minute. diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index b0b393b42..983262160 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -130,7 +130,9 @@ en: already_added: You have already added this investment project already_supported: You have already supported this investment project. Share it! support_title: Support this project - confirm_group: "You can only support investments in one heading. If you continue you cannot change your decision. Are you sure?" + confirm_group: + one: "You can only support investments in %{count} heading. If you continue you cannot change your decision. Are you sure?" + other: "You can only support investments in %{count} headings. If you continue you cannot change your decision. Are you sure?" supports: one: 1 support other: "%{count} supports" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 9fb9fae63..1cbbf2d14 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -119,6 +119,7 @@ es: table_amount: Cantidad table_population: Población population_info: "El campo población de las partidas presupuestarias se usa con fines estadísticos únicamente, con el objetivo de mostrar el porcentaje de votos habidos en cada partida que represente un área con población. Es un campo opcional, así que puedes dejarlo en blanco si no aplica." + max_votable_headings: "Máximo número de partidas en que un usuario puede votar" winners: calculate: Calcular propuestas ganadoras calculated: Calculando ganadoras, puede tardar un minuto. diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index ab1a1e4ac..0b940f2de 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -130,7 +130,9 @@ es: already_added: Ya has añadido este proyecto de gasto already_supported: Ya has apoyado este proyecto de gasto. ¡Compártelo! support_title: Apoyar este proyecto - confirm_group: "Sólo puedes apoyar proyectos de una partida. Si sigues adelante no podrás cambiar esta decisión. ¿Estás seguro?" + confirm_group: + one: "Sólo puedes apoyar proyectos de %{count} partida. Si sigues adelante no podrás cambiar esta decisión. ¿Estás seguro?" + other: "Sólo puedes apoyar proyectos de %{count} partidas. Si sigues adelante no podrás cambiar esta decisión. ¿Estás seguro?" supports: zero: Sin apoyos one: 1 apoyo diff --git a/spec/features/budgets/votes_spec.rb b/spec/features/budgets/votes_spec.rb index 6da622b44..5a56af51b 100644 --- a/spec/features/budgets/votes_spec.rb +++ b/spec/features/budgets/votes_spec.rb @@ -103,5 +103,74 @@ feature 'Votes' do expect(page).not_to have_css("budget_investment_#{investment.id}_votes") end end + + context "Voting in multiple headings of a single group" do + + let(:new_york) { heading } + let(:san_francisco) { create(:budget_heading, group: group) } + let(:third_heading) { create(:budget_heading, group: group) } + + let!(:new_york_investment) { create(:budget_investment, heading: new_york) } + let!(:san_francisco_investment) { create(:budget_investment, heading: san_francisco) } + let!(:third_heading_investment) { create(:budget_investment, heading: third_heading) } + + background do + group.update(max_votable_headings: 2) + end + + scenario "From Index", :js do + visit budget_investments_path(budget, heading_id: new_york.id) + + within("#budget_investment_#{new_york_investment.id}") do + find('.in-favor a').click + + expect(page).to have_content "1 support" + expect(page).to have_content "You have already supported this investment project. Share it!" + end + + visit budget_investments_path(budget, heading_id: san_francisco.id) + + within("#budget_investment_#{san_francisco_investment.id}") do + find('.in-favor a').click + + expect(page).to have_content "1 support" + expect(page).to have_content "You have already supported this investment project. Share it!" + end + + visit budget_investments_path(budget, heading_id: third_heading.id) + + within("#budget_investment_#{third_heading_investment.id}") do + find('.in-favor a').click + + expect(page).to have_content "You can only support investment projects in 2 districts" + + expect(page).to_not have_content "1 support" + expect(page).to_not have_content "You have already supported this investment project. Share it!" + end + end + + scenario "From show", :js do + visit budget_investment_path(budget, new_york_investment) + + find('.in-favor a').click + expect(page).to have_content "1 support" + expect(page).to have_content "You have already supported this investment project. Share it!" + + visit budget_investment_path(budget, san_francisco_investment) + + find('.in-favor a').click + expect(page).to have_content "1 support" + expect(page).to have_content "You have already supported this investment project. Share it!" + + visit budget_investment_path(budget, third_heading_investment) + + find('.in-favor a').click + expect(page).to have_content "You can only support investment projects in 2 districts" + + expect(page).to_not have_content "1 support" + expect(page).to_not have_content "You have already supported this investment project. Share it!" + end + + end end end diff --git a/spec/models/budget/investment_spec.rb b/spec/models/budget/investment_spec.rb index b8b3d423b..73bd46c5d 100644 --- a/spec/models/budget/investment_spec.rb +++ b/spec/models/budget/investment_spec.rb @@ -589,6 +589,20 @@ describe Budget::Investment do expect(salamanca_investment.valid_heading?(user)).to eq(false) end + it "accepts votes in multiple headings of the same group" do + group.update(max_votable_headings: 2) + + carabanchel = create(:budget_heading, group: group) + salamanca = create(:budget_heading, group: group) + + carabanchel_investment = create(:budget_investment, heading: carabanchel) + salamanca_investment = create(:budget_investment, heading: salamanca) + + create(:vote, votable: carabanchel_investment, voter: user) + + expect(salamanca_investment.valid_heading?(user)).to eq(true) + end + it "allows votes in a group with a single heading" do all_city_investment = create(:budget_investment, heading: heading) expect(all_city_investment.valid_heading?(user)).to eq(true) @@ -627,6 +641,35 @@ describe Budget::Investment do expect(carabanchel_investment.valid_heading?(user)).to eq(true) end + + describe "#can_vote_in_another_heading?" do + + let(:districts) { create(:budget_group, budget: budget) } + let(:carabanchel) { create(:budget_heading, group: districts) } + let(:salamanca) { create(:budget_heading, group: districts) } + let(:latina) { create(:budget_heading, group: districts) } + + let(:carabanchel_investment) { create(:budget_investment, heading: carabanchel) } + let(:salamanca_investment) { create(:budget_investment, heading: salamanca) } + let(:latina_investment) { create(:budget_investment, heading: latina) } + + it "returns true if the user has voted in less headings than the maximum" do + districts.update(max_votable_headings: 2) + + create(:vote, votable: carabanchel_investment, voter: user) + + expect(salamanca_investment.can_vote_in_another_heading?(user)).to eq(true) + end + + it "returns false if the user has already voted in the maximum number of headings" do + districts.update(max_votable_headings: 2) + + create(:vote, votable: carabanchel_investment, voter: user) + create(:vote, votable: salamanca_investment, voter: user) + + expect(latina_investment.can_vote_in_another_heading?(user)).to eq(false) + end + end end end From 24fc2f91996bb501f9bb16745ee4b33cb78291d1 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Thu, 22 Mar 2018 21:01:19 +0100 Subject: [PATCH 3/6] Improve translation text and make it a default There was some inconsistency in this file, the `confirm_group` key was in both the custom translations file and the default translations file. Remove duplication and make it a default as it is a clearer message for users. If an installation want to edit this message they can still do it in the custom translations file --- config/i18n-tasks.yml | 2 +- config/locales/en/budgets.yml | 4 ++-- config/locales/en/general.yml | 4 +++- config/locales/es/budgets.yml | 4 ++-- config/locales/es/general.yml | 4 +++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index e778ba104..628e2a15b 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -176,11 +176,11 @@ ignore_unused: - 'admin.site_customization.pages.page.status_*' - 'admin.legislation.processes.process.*' - 'legislation.processes.index.*' + - 'votes.budget_investments.different_heading_assigned*' # - '{devise,kaminari,will_paginate}.*' # - 'simple_form.{yes,no}' # - 'simple_form.{placeholders,hints,labels}.*' # - 'simple_form.{error_notification,required}.:' - ## Exclude these keys from the `i18n-tasks eq-base' report: # ignore_eq_base: # all: diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index 983262160..48d3b3462 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -131,8 +131,8 @@ en: already_supported: You have already supported this investment project. Share it! support_title: Support this project confirm_group: - one: "You can only support investments in %{count} heading. If you continue you cannot change your decision. Are you sure?" - other: "You can only support investments in %{count} headings. If you continue you cannot change your decision. Are you sure?" + one: "You can only support investments in %{count} district. If you continue you cannot change the election of your district. Are you sure?" + other: "You can only support investments in %{count} district. If you continue you cannot change the election of your district. Are you sure?" supports: one: 1 support other: "%{count} supports" diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index 01ba0c2c3..66f392921 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -771,7 +771,9 @@ en: organization: Organizations are not permitted to vote unfeasible: Unfeasible investment projects can not be supported not_voting_allowed: Voting phase is closed - different_heading_assigned: You can only support investment projects in one heading + different_heading_assigned: + one: "You can only support investment projects in %{count} district" + other: "You can only support investment projects in %{count} districts" welcome: debates: description: For meeting, discussing and sharing the things that matter to us in our city. diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index 0b940f2de..67ad55d5f 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -131,8 +131,8 @@ es: already_supported: Ya has apoyado este proyecto de gasto. ¡Compártelo! support_title: Apoyar este proyecto confirm_group: - one: "Sólo puedes apoyar proyectos de %{count} partida. Si sigues adelante no podrás cambiar esta decisión. ¿Estás seguro?" - other: "Sólo puedes apoyar proyectos de %{count} partidas. Si sigues adelante no podrás cambiar esta decisión. ¿Estás seguro?" + one: "Sólo puedes apoyar proyectos en %{count} distritos. Si sigues adelante no podrás cambiar la elección de este distrito. ¿Estás seguro?" + other: "Sólo puedes apoyar proyectos en %{count} distritos. Si sigues adelante no podrás cambiar la elección de este distrito. ¿Estás seguro?" supports: zero: Sin apoyos one: 1 apoyo diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index 2c3113e5f..08bbbdba6 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -770,7 +770,9 @@ es: organization: Las organizaciones no pueden votar. unfeasible: No se pueden votar propuestas inviables. not_voting_allowed: El periodo de votación está cerrado. - different_heading_assigned: Sólo puedes apoyar proyectos de gasto de una partida + different_heading_assigned: + one: "Sólo puedes apoyar proyectos de gasto de %{count} distrito" + other: "Sólo puedes apoyar proyectos de gasto de %{count} distritos" welcome: debates: description: Encontrarnos, debatir y compartir lo que nos parece importante en nuestra ciudad. From 0398bb23cba2633496428b86bbaf9c42772d59ae Mon Sep 17 00:00:00 2001 From: rgarcia Date: Thu, 22 Mar 2018 21:29:19 +0100 Subject: [PATCH 4/6] Consistent spacing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Temporarily… because there are 2 kinds of Ruby developers, those who indent methods under `private` and does who don’t https://gist.github.com/joefiorini/1049083#gistcomment-37692 --- app/models/budget/group.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/budget/group.rb b/app/models/budget/group.rb index a32c7c90d..bfbfd4741 100644 --- a/app/models/budget/group.rb +++ b/app/models/budget/group.rb @@ -16,9 +16,9 @@ class Budget private - def generate_slug? - slug.nil? || budget.drafting? - end + def generate_slug? + slug.nil? || budget.drafting? + end end end From 5c6eaa76ff95c07f52d525e971cc45d1a8569374 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Thu, 22 Mar 2018 22:24:38 +0100 Subject: [PATCH 5/6] Add headings_voted_by_user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This method was used only in Madrid’s fork, but it is now needed to complete the backport for voting in multiple headings There wasn’t a test in Madrid, so here goes one too. Even though, the responsibility should probably be moved soon to the `Budget::Heading`. For consistency with the related methods and tests it has been left in the investment_spec --- app/models/budget/investment.rb | 4 ++++ spec/models/budget/investment_spec.rb | 29 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index 28cc5bded..6e4a9f477 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -239,6 +239,10 @@ class Budget headings_voted_by_user(user).count < group.max_votable_headings end + def headings_voted_by_user(user) + user.votes.for_budget_investments(budget.investments.where(group: group)).votables.map(&:heading_id).uniq + end + def voted_in?(heading_ids, user) heading_ids.include? heading_voted_by_user?(user) end diff --git a/spec/models/budget/investment_spec.rb b/spec/models/budget/investment_spec.rb index 73bd46c5d..d277647de 100644 --- a/spec/models/budget/investment_spec.rb +++ b/spec/models/budget/investment_spec.rb @@ -673,6 +673,35 @@ describe Budget::Investment do end end + describe "#headings_voted_by_user" do + it "returns the headings voted by a user" do + user1 = create(:user) + user2 = create(:user) + + budget = create(:budget) + group = create(:budget_group, budget: budget) + + new_york = create(:budget_heading, group: group) + san_franciso = create(:budget_heading, group: group) + another_heading = create(:budget_heading, group: group) + + new_york_investment = create(:budget_investment, heading: new_york) + san_franciso_investment = create(:budget_investment, heading: san_franciso) + another_investment = create(:budget_investment, heading: san_franciso) + + create(:vote, votable: new_york_investment, voter: user1) + create(:vote, votable: san_franciso_investment, voter: user1) + + expect(another_investment.headings_voted_by_user(user1)).to include(new_york.id) + expect(another_investment.headings_voted_by_user(user1)).to include(san_franciso.id) + expect(another_investment.headings_voted_by_user(user1)).to_not include(another_heading.id) + + expect(another_investment.headings_voted_by_user(user2)).to_not include(new_york.id) + expect(another_investment.headings_voted_by_user(user2)).to_not include(san_franciso.id) + expect(another_investment.headings_voted_by_user(user2)).to_not include(another_heading.id) + end + end + describe "Order" do describe "#sort_by_confidence_score" do From 073cf748182df8128e1d992283570a8fab01cf59 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Thu, 22 Mar 2018 23:08:41 +0100 Subject: [PATCH 6/6] Fix edge case The user was able to vote as many investments as wanted in the first heading voted. However in the second heading voted, only one investment could be voted This was due to the previous implementation, where you could only vote in one heading. Note the `first` call in method `heading_voted_by_user?(user)` This commits simplifies the logic and allows voting for any investment in any heading that the user has previously voted in --- app/models/budget/investment.rb | 11 +++------ spec/models/budget/investment_spec.rb | 33 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index 6e4a9f477..53d27a741 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -231,7 +231,7 @@ class Budget end def valid_heading?(user) - voted_in?([heading.id], user) || + voted_in?(heading, user) || can_vote_in_another_heading?(user) end @@ -243,13 +243,8 @@ class Budget user.votes.for_budget_investments(budget.investments.where(group: group)).votables.map(&:heading_id).uniq end - def voted_in?(heading_ids, user) - heading_ids.include? heading_voted_by_user?(user) - end - - def heading_voted_by_user?(user) - user.votes.for_budget_investments(budget.investments.where(group: group)) - .votables.map(&:heading_id).first + def voted_in?(heading, user) + headings_voted_by_user(user).include?(heading.id) end def ballotable_by?(user) diff --git a/spec/models/budget/investment_spec.rb b/spec/models/budget/investment_spec.rb index d277647de..cdee81087 100644 --- a/spec/models/budget/investment_spec.rb +++ b/spec/models/budget/investment_spec.rb @@ -603,6 +603,22 @@ describe Budget::Investment do expect(salamanca_investment.valid_heading?(user)).to eq(true) end + it "accepts votes in any heading previously voted in" do + group.update(max_votable_headings: 2) + + carabanchel = create(:budget_heading, group: group) + salamanca = create(:budget_heading, group: group) + + carabanchel_investment = create(:budget_investment, heading: carabanchel) + salamanca_investment = create(:budget_investment, heading: salamanca) + + create(:vote, votable: carabanchel_investment, voter: user) + create(:vote, votable: salamanca_investment, voter: user) + + expect(carabanchel_investment.valid_heading?(user)).to eq(true) + expect(salamanca_investment.valid_heading?(user)).to eq(true) + end + it "allows votes in a group with a single heading" do all_city_investment = create(:budget_investment, heading: heading) expect(all_city_investment.valid_heading?(user)).to eq(true) @@ -702,6 +718,23 @@ describe Budget::Investment do end end + describe "#voted_in?" do + + let(:user) { create(:user) } + let(:investment) { create(:budget_investment) } + + it "returns true if the user has voted in this heading" do + create(:vote, votable: investment, voter: user) + + expect(investment.voted_in?(investment.heading, user)).to eq(true) + end + + it "returns false if the user has not voted in this heading" do + expect(investment.voted_in?(investment.heading, user)).to eq(false) + end + + end + describe "Order" do describe "#sort_by_confidence_score" do