From b0358e48671ddd23e4fd04c32e3b9994540b6e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Mon, 5 Sep 2016 17:50:58 +0200 Subject: [PATCH 1/8] adds index of investments with filters to admin --- .../admin/budget_investments_controller.rb | 15 + app/helpers/budget_groups_helper.rb | 7 + .../admin/budget_investments/index.html.erb | 76 +++++ .../admin/budget_investments/show.html.erb | 1 + app/views/admin/budgets/index.html.erb | 5 +- config/i18n-tasks.yml | 1 + config/locales/activerecord.es.yml | 3 + config/locales/admin.en.yml | 22 ++ config/locales/admin.es.yml | 22 ++ config/routes.rb | 2 + .../features/admin/budget_investments_spec.rb | 298 ++++++++++++++++++ spec/features/admin/budgets_spec.rb | 7 +- 12 files changed, 456 insertions(+), 3 deletions(-) create mode 100644 app/controllers/admin/budget_investments_controller.rb create mode 100644 app/helpers/budget_groups_helper.rb create mode 100644 app/views/admin/budget_investments/index.html.erb create mode 100644 app/views/admin/budget_investments/show.html.erb create mode 100644 spec/features/admin/budget_investments_spec.rb diff --git a/app/controllers/admin/budget_investments_controller.rb b/app/controllers/admin/budget_investments_controller.rb new file mode 100644 index 000000000..bf15ee7f6 --- /dev/null +++ b/app/controllers/admin/budget_investments_controller.rb @@ -0,0 +1,15 @@ +class Admin::BudgetInvestmentsController < Admin::BaseController + + has_filters %w{valuation_open without_admin managed valuating valuation_finished all}, only: :index + + def index + @budget = Budget.includes(:groups).find params[:budget_id] + @investments = @budget.investments.scoped_filter(params, @current_filter).order(cached_votes_up: :desc, created_at: :desc).page(params[:page]) + end + + def show + @budget = Budget.includes(:groups).find params[:budget_id] + @investment = @budget.investments.find params[:id] + end + +end \ No newline at end of file diff --git a/app/helpers/budget_groups_helper.rb b/app/helpers/budget_groups_helper.rb new file mode 100644 index 000000000..d36a992d9 --- /dev/null +++ b/app/helpers/budget_groups_helper.rb @@ -0,0 +1,7 @@ +module BudgetGroupsHelper + + def budget_group_select_options(groups) + groups.map {|group| [group.name, group.id]} + end + +end \ No newline at end of file diff --git a/app/views/admin/budget_investments/index.html.erb b/app/views/admin/budget_investments/index.html.erb new file mode 100644 index 000000000..31ad2298a --- /dev/null +++ b/app/views/admin/budget_investments/index.html.erb @@ -0,0 +1,76 @@ +

<%= @budget.name %> - <%= t("admin.budget_investments.index.title") %>

+ +
+ <%= form_tag admin_budget_budget_investments_path(budget: @budget), method: :get, enforce_utf8: false do %> +
+ <%= select_tag :administrator_id, + options_for_select(admin_select_options, params[:administrator_id]), + { prompt: t("admin.budget_investments.index.administrator_filter_all"), + label: false, + class: "js-submit-on-change" } %> +
+ +
+ <%= select_tag :valuator_id, + options_for_select(valuator_select_options, params[:valuator_id]), + { prompt: t("admin.budget_investments.index.valuator_filter_all"), + label: false, + class: "js-submit-on-change" } %> +
+ +
+ <%= select_tag :group_id, + options_for_select(budget_group_select_options(@budget.groups), params[:group_id]), + { prompt: t("admin.budget_investments.index.group_filter_all"), + label: false, + class: "js-submit-on-change" } %> +
+ +
+ <%= select_tag :tag_name, + options_for_select(spending_proposal_tags_select_options, params[:tag_name]), + { prompt: t("admin.budget_investments.index.tags_filter_all"), + label: false, + class: "js-submit-on-change" } %> +
+ <% end %> +
+ +<%= render 'shared/filter_subnav', i18n_namespace: "admin.budget_investments.index" %> + +

<%= page_entries_info @investments %>

+ + + <% @investments.each do |investment| %> + + + + + + + + + <% end %> +
+ <%= investment.id %> + + <%= link_to investment.title, admin_budget_budget_investment_path(budget_id: @budget.id, id: investment.id), Budget::Investment.filter_params(params) %> + + <% if investment.administrator.present? %> + <%= investment.administrator.name %> + <% else %> + <%= t("admin.budget_investments.index.no_admin_assigned") %> + <% end %> + + <% if investment.valuators.size == 0 %> + <%= t("admin.budget_investments.index.no_valuators_assigned") %> + <% else %> + <%= investment.valuators.collect(&:description_or_name).join(', ') %> + <% end %> + + <%= investment.group.name %> + + <%= t("admin.budget_investments.index.feasibility.#{investment.feasibility}", price: investment.price) %> +
+ +<%= paginate @investments %> \ No newline at end of file diff --git a/app/views/admin/budget_investments/show.html.erb b/app/views/admin/budget_investments/show.html.erb new file mode 100644 index 000000000..a142a388d --- /dev/null +++ b/app/views/admin/budget_investments/show.html.erb @@ -0,0 +1 @@ +<%= @investment.title %> \ No newline at end of file diff --git a/app/views/admin/budgets/index.html.erb b/app/views/admin/budgets/index.html.erb index 193b6a7ef..d4de771db 100644 --- a/app/views/admin/budgets/index.html.erb +++ b/app/views/admin/budgets/index.html.erb @@ -13,11 +13,14 @@ <% @budgets.each do |budget| %> - <%= link_to budget.name, admin_budget_path(budget) %> + <%= link_to budget.name, admin_budget_budget_investments_path(budget_id: budget.id) %> <%= t("budget.phase.#{budget.phase}") %> + + <%= link_to t("admin.budgets.index.info_link"), admin_budget_path(budget) %> + <% end %> diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 7df6f7f9e..161ed6dfd 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -117,6 +117,7 @@ ignore_unused: - 'admin.debates.index.filter*' - 'admin.proposals.index.filter*' - 'admin.budgets.index.filter*' + - 'admin.budget_investments.index.filter*' - 'admin.spending_proposals.index.filter*' - 'admin.organizations.index.filter*' - 'admin.users.index.filter*' diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index ccd0240e8..c7c811801 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -7,6 +7,9 @@ es: budget: one: "Presupuesto participativo" other: "Presupuestos participativos" + budget/investment: + one: "Propuesta de inversión" + other: "Propuestas de inversión" comment: one: "Comentario" other: "Comentarios" diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index 78a3a6d6c..61845a164 100755 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -62,6 +62,7 @@ en: index: title: Participatory budgets new_link: Create new + info_link: Info filters: open: Open finished: Finished @@ -92,6 +93,27 @@ en: table_heading: Heading table_amount: Amount table_geozone: Scope of operation + budget_investments: + index: + group_filter_all: All voting groups + administrator_filter_all: All administrators + valuator_filter_all: All valuators + tags_filter_all: All tags + filters: + valuation_open: Open + without_admin: Without assigned admin + managed: Managed + valuating: Under valuation + valuation_finished: Valuation finished + all: All + title: Investment projects + assigned_admin: Assigned administrator + no_admin_assigned: No admin assigned + no_valuators_assigned: No valuators assigned + feasibility: + feasible: "Feasible (%{price})" + not_feasible: "Not feasible" + undefined: "Undefined" comments: index: filter: Filter diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 496a0208d..7d2417480 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -62,6 +62,7 @@ es: index: title: Presupuestos participativos new_link: Crear nuevo + info_link: Info filters: open: Abiertos finished: Terminados @@ -92,6 +93,27 @@ es: table_heading: Partida table_amount: Cantidad table_geozone: Ámbito de actuación + budget_investments: + index: + group_filter_all: Todos los grupos + administrator_filter_all: Todos los administradores + valuator_filter_all: Todos los evaluadores + tags_filter_all: Todas las etiquetas + filters: + valuation_open: Abiertas + without_admin: Sin administrador + managed: Gestionando + valuating: En evaluación + valuation_finished: Evaluación finalizada + all: Todas + title: Propuestas de inversión + assigned_admin: Administrador asignado + no_admin_assigned: Sin admin asignado + no_valuators_assigned: Sin evaluador + feasibility: + feasible: "Viable (%{price})" + not_feasible: "Inviable" + undefined: "Sin definir" comments: index: filter: Filtro diff --git a/config/routes.rb b/config/routes.rb index 92f3d6c22..278153a19 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -161,6 +161,8 @@ Rails.application.routes.draw do resources :budget_headings do end end + + resources :budget_investments, only: [:index, :show] end resources :banners, only: [:index, :new, :create, :edit, :update, :destroy] do diff --git a/spec/features/admin/budget_investments_spec.rb b/spec/features/admin/budget_investments_spec.rb new file mode 100644 index 000000000..3c5e611c5 --- /dev/null +++ b/spec/features/admin/budget_investments_spec.rb @@ -0,0 +1,298 @@ +require 'rails_helper' + +feature 'Admin budget investments' do + + background do + admin = create(:administrator) + login_as(admin.user) + + @budget = create(:budget) + end + + context "Index" do + + scenario 'Displaying investmentss' do + budget_investment = create(:budget_investment, budget: @budget) + visit admin_budget_budget_investments_path(budget_id: @budget.id) + expect(page).to have_content(budget_investment.title) + end + + scenario 'Displaying assignments info' do + budget_investment1 = create(:budget_investment, budget: @budget) + budget_investment2 = create(:budget_investment, budget: @budget) + budget_investment3 = create(:budget_investment, budget: @budget) + + valuator1 = create(:valuator, user: create(:user, username: 'Olga'), description: 'Valuator Olga') + valuator2 = create(:valuator, user: create(:user, username: 'Miriam'), description: 'Valuator Miriam') + admin = create(:administrator, user: create(:user, username: 'Gema')) + + budget_investment1.valuators << valuator1 + budget_investment2.valuator_ids = [valuator1.id, valuator2.id] + budget_investment3.update({administrator_id: admin.id}) + + visit admin_budget_budget_investments_path(budget_id: @budget.id) + + within("#budget_investment_#{budget_investment1.id}") do + expect(page).to have_content("No admin assigned") + expect(page).to have_content("Valuator Olga") + end + + within("#budget_investment_#{budget_investment2.id}") do + expect(page).to have_content("No admin assigned") + expect(page).to have_content("Valuator Olga") + expect(page).to have_content("Valuator Miriam") + end + + within("#budget_investment_#{budget_investment3.id}") do + expect(page).to have_content("Gema") + expect(page).to have_content("No valuators assigned") + end + end + + scenario "Filtering by budget group", :js do + group1 = create(:budget_group, name: "Street improvments", budget: @budget) + group2 = create(:budget_group, name: "Parks", budget: @budget) + + create(:budget_investment, title: "Realocate visitors", budget: @budget, group: group1) + create(:budget_investment, title: "Destroy the city", budget: @budget, group: group2) + + visit admin_budget_budget_investments_path(budget_id: @budget.id) + + expect(page).to have_link("Realocate visitors") + expect(page).to have_link("Destroy the city") + + select "Parks", from: "group_id" + + expect(page).to have_link("Destroy the city") + expect(page).to_not have_link("Realocate visitors") + + select "All voting groups", from: "group_id" + + expect(page).to have_link("Realocate visitors") + expect(page).to have_link("Destroy the city") + + select "Street improvments", from: "group_id" + + expect(page).to_not have_link("Destroy the city") + expect(page).to have_link("Realocate visitors") + + # click_link("Realocate visitors") + # click_link("Back") + + # expect(page).to_not have_link("Destroy the city") + # expect(page).to have_link("Realocate visitors") + + # click_link("Realocate visitors") + # click_link("Edit classification") + # expect(page).to have_button("Update") + # click_link("Back") + # expect(page).to_not have_button("Update") + # click_link("Back") + + # expect(page).to_not have_link("Destroy the city") + # expect(page).to have_link("Realocate visitors") + end + + scenario "Filtering by admin", :js do + user = create(:user, username: 'Admin 1') + administrator = create(:administrator, user: user) + + create(:budget_investment, title: "Realocate visitors", budget: @budget, administrator: administrator) + create(:budget_investment, title: "Destroy the city", budget: @budget) + + visit admin_budget_budget_investments_path(budget_id: @budget.id) + expect(page).to have_link("Realocate visitors") + expect(page).to have_link("Destroy the city") + + select "Admin 1", from: "administrator_id" + + expect(page).to have_content('There is 1 investment') + expect(page).to_not have_link("Destroy the city") + expect(page).to have_link("Realocate visitors") + + select "All administrators", from: "administrator_id" + + expect(page).to have_content('There are 2 investments') + expect(page).to have_link("Destroy the city") + expect(page).to have_link("Realocate visitors") + + select "Admin 1", from: "administrator_id" + expect(page).to have_content('There is 1 investment') + + # click_link("Realocate visitors") + # click_link("Back") + + # expect(page).to have_content('There is 1 investment') + # expect(page).to_not have_link("Destroy the city") + # expect(page).to have_link("Realocate visitors") + + # click_link("Realocate visitors") + # click_link("Edit classification") + # expect(page).to have_button("Update") + # click_link("Back") + # expect(page).to_not have_button("Update") + # click_link("Back") + + # expect(page).to have_content('There is 1 investment') + # expect(page).to_not have_link("Destroy the city") + # expect(page).to have_link("Realocate visitors") + end + + scenario "Filtering by valuator", :js do + user = create(:user) + valuator = create(:valuator, user: user, description: 'Valuator 1') + + budget_investment = create(:budget_investment, title: "Realocate visitors", budget: @budget) + budget_investment.valuators << valuator + + create(:budget_investment, title: "Destroy the city", budget: @budget) + + visit admin_budget_budget_investments_path(budget_id: @budget.id) + expect(page).to have_link("Realocate visitors") + expect(page).to have_link("Destroy the city") + + select "Valuator 1", from: "valuator_id" + + expect(page).to have_content('There is 1 investment') + expect(page).to_not have_link("Destroy the city") + expect(page).to have_link("Realocate visitors") + + select "All valuators", from: "valuator_id" + + expect(page).to have_content('There are 2 investments') + expect(page).to have_link("Destroy the city") + expect(page).to have_link("Realocate visitors") + + select "Valuator 1", from: "valuator_id" + expect(page).to have_content('There is 1 investment') + # click_link("Realocate visitors") + # click_link("Back") + + # expect(page).to have_content('There is 1 investment') + # expect(page).to_not have_link("Destroy the city") + # expect(page).to have_link("Realocate visitors") + + # click_link("Realocate visitors") + # click_link("Edit classification") + # expect(page).to have_button("Update") + # click_link("Back") + # expect(page).to_not have_button("Update") + # click_link("Back") + + # expect(page).to have_content('There is 1 investment') + # expect(page).to_not have_link("Destroy the city") + # expect(page).to have_link("Realocate visitors") + end + + scenario "Current filter is properly highlighted" do + filters_links = {'valuation_open' => 'Open', + 'without_admin' => 'Without assigned admin', + 'managed' => 'Managed', + 'valuating' => 'Under valuation', + 'valuation_finished' => 'Valuation finished', + 'all' => 'All'} + + visit admin_budget_budget_investments_path(budget_id: @budget.id) + + expect(page).to_not have_link(filters_links.values.first) + filters_links.keys.drop(1).each { |filter| expect(page).to have_link(filters_links[filter]) } + + filters_links.each_pair do |current_filter, link| + visit admin_budget_budget_investments_path(budget_id: @budget.id, filter: current_filter) + + expect(page).to_not have_link(link) + + (filters_links.keys - [current_filter]).each do |filter| + expect(page).to have_link(filters_links[filter]) + end + end + end + + scenario "Filtering by assignment status" do + assigned = create(:budget_investment, title: "Assigned idea", budget: @budget, administrator: create(:administrator)) + valuating = create(:budget_investment, title: "Evaluating...", budget: @budget) + valuating.valuators << create(:valuator) + + visit admin_budget_budget_investments_path(budget_id: @budget.id, filter: 'valuation_open') + + expect(page).to have_content("Assigned idea") + expect(page).to have_content("Evaluating...") + + visit admin_budget_budget_investments_path(budget_id: @budget.id, filter: 'without_admin') + + expect(page).to have_content("Evaluating...") + expect(page).to_not have_content("Assigned idea") + + visit admin_budget_budget_investments_path(budget_id: @budget.id, filter: 'managed') + + expect(page).to have_content("Assigned idea") + expect(page).to_not have_content("Evaluating...") + end + + scenario "Filtering by valuation status" do + valuating = create(:budget_investment, budget: @budget, title: "Ongoing valuation") + valuated = create(:budget_investment, budget: @budget, title: "Old idea", valuation_finished: true) + valuating.valuators << create(:valuator) + valuated.valuators << create(:valuator) + + visit admin_budget_budget_investments_path(budget_id: @budget.id, filter: 'valuation_open') + + expect(page).to have_content("Ongoing valuation") + expect(page).to_not have_content("Old idea") + + visit admin_budget_budget_investments_path(budget_id: @budget.id, filter: 'valuating') + + expect(page).to have_content("Ongoing valuation") + expect(page).to_not have_content("Old idea") + + visit admin_budget_budget_investments_path(budget_id: @budget.id, filter: 'valuation_finished') + + expect(page).to_not have_content("Ongoing valuation") + expect(page).to have_content("Old idea") + + visit admin_budget_budget_investments_path(budget_id: @budget.id, filter: 'all') + expect(page).to have_content("Ongoing valuation") + expect(page).to have_content("Old idea") + end + + scenario "Filtering by tag" do + create(:budget_investment, budget: @budget, title: 'Educate the children', tag_list: 'Education') + create(:budget_investment, budget: @budget, title: 'More schools', tag_list: 'Education') + create(:budget_investment, budget: @budget, title: 'More hospitals', tag_list: 'Health') + + visit admin_budget_budget_investments_path(budget_id: @budget.id) + + expect(page).to have_css(".budget_investment", count: 3) + expect(page).to have_content("Educate the children") + expect(page).to have_content("More schools") + expect(page).to have_content("More hospitals") + + visit admin_budget_budget_investments_path(budget_id: @budget.id, tag_name: 'Education') + + expect(page).to_not have_content("More hospitals") + expect(page).to have_css(".budget_investment", count: 2) + expect(page).to have_content("Educate the children") + expect(page).to have_content("More schools") + + # click_link("Educate the children") + # click_link("Back") + + # expect(page).to_not have_content("More hospitals") + # expect(page).to have_content("Educate the children") + # expect(page).to have_content("More schools") + + # click_link("Educate the children") + # click_link("Edit classification") + # expect(page).to have_button("Update") + # click_link("Back") + # expect(page).to_not have_button("Update") + # click_link("Back") + + # expect(page).to_not have_content("More hospitals") + # expect(page).to have_content("Educate the children") + # expect(page).to have_content("More schools") + end + + end + +end \ No newline at end of file diff --git a/spec/features/admin/budgets_spec.rb b/spec/features/admin/budgets_spec.rb index 123ca43e8..a87cd741a 100644 --- a/spec/features/admin/budgets_spec.rb +++ b/spec/features/admin/budgets_spec.rb @@ -105,10 +105,13 @@ feature 'Admin budgets' do context 'Manage groups and headings' do scenario 'Create group', :js do - create(:budget, name: 'Yearly participatory budget') + budget = create(:budget, name: 'Yearly participatory budget') visit admin_budgets_path - click_link 'Yearly participatory budget' + + within("#budget_#{budget.id}") do + click_link 'Info' + end expect(page).to have_content 'No groups created yet.' From 31ef3f4c9815e8d2c5bd641c711f15ec990fb454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 6 Sep 2016 17:03:35 +0200 Subject: [PATCH 2/8] adds heading filter to index --- app/helpers/budget_groups_helper.rb | 7 -- app/helpers/budget_headings_helper.rb | 7 ++ app/models/budget/investment.rb | 2 +- .../admin/budget_investments/index.html.erb | 10 +- config/locales/admin.en.yml | 2 +- config/locales/admin.es.yml | 2 +- .../features/admin/budget_investments_spec.rb | 92 +++++++++++-------- 7 files changed, 69 insertions(+), 53 deletions(-) delete mode 100644 app/helpers/budget_groups_helper.rb create mode 100644 app/helpers/budget_headings_helper.rb diff --git a/app/helpers/budget_groups_helper.rb b/app/helpers/budget_groups_helper.rb deleted file mode 100644 index d36a992d9..000000000 --- a/app/helpers/budget_groups_helper.rb +++ /dev/null @@ -1,7 +0,0 @@ -module BudgetGroupsHelper - - def budget_group_select_options(groups) - groups.map {|group| [group.name, group.id]} - end - -end \ No newline at end of file diff --git a/app/helpers/budget_headings_helper.rb b/app/helpers/budget_headings_helper.rb new file mode 100644 index 000000000..cb042f796 --- /dev/null +++ b/app/helpers/budget_headings_helper.rb @@ -0,0 +1,7 @@ +module BudgetHeadingsHelper + + def budget_heading_select_options(budget) + budget.headings.map {|heading| [heading.name, heading.id]} + end + +end \ No newline at end of file diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index f76d1f68e..56ae67d41 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -56,7 +56,7 @@ class Budget before_validation :set_responsible_name def self.filter_params(params) - params.select{|x,_| %w{heading_id administrator_id tag_name valuator_id}.include? x.to_s } + params.select{|x,_| %w{heading_id group_id administrator_id tag_name valuator_id}.include? x.to_s } end def self.scoped_filter(params, current_filter) diff --git a/app/views/admin/budget_investments/index.html.erb b/app/views/admin/budget_investments/index.html.erb index 31ad2298a..5aff40115 100644 --- a/app/views/admin/budget_investments/index.html.erb +++ b/app/views/admin/budget_investments/index.html.erb @@ -19,9 +19,9 @@
- <%= select_tag :group_id, - options_for_select(budget_group_select_options(@budget.groups), params[:group_id]), - { prompt: t("admin.budget_investments.index.group_filter_all"), + <%= select_tag :heading_id, + options_for_select(budget_heading_select_options(@budget), params[:heading_id]), + { prompt: t("admin.budget_investments.index.heading_filter_all"), label: false, class: "js-submit-on-change" } %>
@@ -47,7 +47,7 @@ <%= investment.id %> - <%= link_to investment.title, admin_budget_budget_investment_path(budget_id: @budget.id, id: investment.id), Budget::Investment.filter_params(params) %> + <%= link_to investment.title, admin_budget_budget_investment_path(budget_id: @budget.id, id: investment.id, params: Budget::Investment.filter_params(params)) %> <% if investment.administrator.present? %> @@ -64,7 +64,7 @@ <% end %> - <%= investment.group.name %> + <%= investment.heading.name %> <%= t("admin.budget_investments.index.feasibility.#{investment.feasibility}", price: investment.price) %> diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index 61845a164..4a9abf7d3 100755 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -95,7 +95,7 @@ en: table_geozone: Scope of operation budget_investments: index: - group_filter_all: All voting groups + heading_filter_all: All headings administrator_filter_all: All administrators valuator_filter_all: All valuators tags_filter_all: All tags diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 7d2417480..3600c2448 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -95,7 +95,7 @@ es: table_geozone: Ámbito de actuación budget_investments: index: - group_filter_all: Todos los grupos + heading_filter_all: Todas las partidas administrator_filter_all: Todos los administradores valuator_filter_all: Todos los evaluadores tags_filter_all: Todas las etiquetas diff --git a/spec/features/admin/budget_investments_spec.rb b/spec/features/admin/budget_investments_spec.rb index 3c5e611c5..ad08ee634 100644 --- a/spec/features/admin/budget_investments_spec.rb +++ b/spec/features/admin/budget_investments_spec.rb @@ -49,45 +49,61 @@ feature 'Admin budget investments' do end end - scenario "Filtering by budget group", :js do - group1 = create(:budget_group, name: "Street improvments", budget: @budget) + scenario "Filtering by budget heading", :js do + group1 = create(:budget_group, name: "Streets", budget: @budget) group2 = create(:budget_group, name: "Parks", budget: @budget) - create(:budget_investment, title: "Realocate visitors", budget: @budget, group: group1) - create(:budget_investment, title: "Destroy the city", budget: @budget, group: group2) + group1_heading1 = create(:budget_heading, group: group1, name: "Main Avenue") + group1_heading2 = create(:budget_heading, group: group1, name: "Mercy Street") + group2_heading1 = create(:budget_heading, group: group2, name: "Central Park") + + create(:budget_investment, title: "Realocate visitors", budget: @budget, group: group1, heading: group1_heading1) + create(:budget_investment, title: "Change name", budget: @budget, group: group1, heading: group1_heading2) + create(:budget_investment, title: "Plant trees", budget: @budget, group: group2, heading: group2_heading1) visit admin_budget_budget_investments_path(budget_id: @budget.id) expect(page).to have_link("Realocate visitors") - expect(page).to have_link("Destroy the city") + expect(page).to have_link("Change name") + expect(page).to have_link("Plant trees") - select "Parks", from: "group_id" + select "Central Park", from: "heading_id" - expect(page).to have_link("Destroy the city") expect(page).to_not have_link("Realocate visitors") + expect(page).to_not have_link("Change name") + expect(page).to have_link("Plant trees") - select "All voting groups", from: "group_id" + select "All headings", from: "heading_id" expect(page).to have_link("Realocate visitors") - expect(page).to have_link("Destroy the city") + expect(page).to have_link("Change name") + expect(page).to have_link("Plant trees") - select "Street improvments", from: "group_id" + select "Main Avenue", from: "heading_id" - expect(page).to_not have_link("Destroy the city") expect(page).to have_link("Realocate visitors") + expect(page).to_not have_link("Change name") + expect(page).to_not have_link("Plant trees") - # click_link("Realocate visitors") - # click_link("Back") + select "Mercy Street", from: "heading_id" - # expect(page).to_not have_link("Destroy the city") - # expect(page).to have_link("Realocate visitors") + expect(page).to_not have_link("Realocate visitors") + expect(page).to have_link("Change name") + expect(page).to_not have_link("Plant trees") + + click_link("Change name") + click_link("Go back") + + expect(page).to_not have_link("Realocate visitors") + expect(page).to have_link("Change name") + expect(page).to_not have_link("Plant trees") # click_link("Realocate visitors") # click_link("Edit classification") # expect(page).to have_button("Update") - # click_link("Back") + # click_link("Go back") # expect(page).to_not have_button("Update") - # click_link("Back") + # click_link("Go back") # expect(page).to_not have_link("Destroy the city") # expect(page).to have_link("Realocate visitors") @@ -119,19 +135,19 @@ feature 'Admin budget investments' do select "Admin 1", from: "administrator_id" expect(page).to have_content('There is 1 investment') - # click_link("Realocate visitors") - # click_link("Back") + click_link("Realocate visitors") + click_link("Go back") - # expect(page).to have_content('There is 1 investment') - # expect(page).to_not have_link("Destroy the city") - # expect(page).to have_link("Realocate visitors") + expect(page).to have_content('There is 1 investment') + expect(page).to_not have_link("Destroy the city") + expect(page).to have_link("Realocate visitors") # click_link("Realocate visitors") # click_link("Edit classification") # expect(page).to have_button("Update") - # click_link("Back") + # click_link("Go back") # expect(page).to_not have_button("Update") - # click_link("Back") + # click_link("Go back") # expect(page).to have_content('There is 1 investment') # expect(page).to_not have_link("Destroy the city") @@ -165,19 +181,19 @@ feature 'Admin budget investments' do select "Valuator 1", from: "valuator_id" expect(page).to have_content('There is 1 investment') - # click_link("Realocate visitors") - # click_link("Back") + click_link("Realocate visitors") + click_link("Go back") - # expect(page).to have_content('There is 1 investment') - # expect(page).to_not have_link("Destroy the city") - # expect(page).to have_link("Realocate visitors") + expect(page).to have_content('There is 1 investment') + expect(page).to_not have_link("Destroy the city") + expect(page).to have_link("Realocate visitors") # click_link("Realocate visitors") # click_link("Edit classification") # expect(page).to have_button("Update") - # click_link("Back") + # click_link("Go back") # expect(page).to_not have_button("Update") - # click_link("Back") + # click_link("Go back") # expect(page).to have_content('There is 1 investment') # expect(page).to_not have_link("Destroy the city") @@ -274,19 +290,19 @@ feature 'Admin budget investments' do expect(page).to have_content("Educate the children") expect(page).to have_content("More schools") - # click_link("Educate the children") - # click_link("Back") + click_link("Educate the children") + click_link("Go back") - # expect(page).to_not have_content("More hospitals") - # expect(page).to have_content("Educate the children") - # expect(page).to have_content("More schools") + expect(page).to_not have_content("More hospitals") + expect(page).to have_content("Educate the children") + expect(page).to have_content("More schools") # click_link("Educate the children") # click_link("Edit classification") # expect(page).to have_button("Update") - # click_link("Back") + # click_link("Go back") # expect(page).to_not have_button("Update") - # click_link("Back") + # click_link("Go back") # expect(page).to_not have_content("More hospitals") # expect(page).to have_content("Educate the children") From 65428126479b86926191c1c02a630fcc383da2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 6 Sep 2016 17:04:31 +0200 Subject: [PATCH 3/8] adds simple investment show view to admin valuators info is missing --- .../admin/budget_investments_controller.rb | 14 ++++-- .../_written_by_author.html.erb | 36 +++++++++++++ .../admin/budget_investments/show.html.erb | 50 ++++++++++++++++++- config/locales/admin.en.yml | 15 ++++++ config/locales/admin.es.yml | 15 ++++++ 5 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 app/views/admin/budget_investments/_written_by_author.html.erb diff --git a/app/controllers/admin/budget_investments_controller.rb b/app/controllers/admin/budget_investments_controller.rb index bf15ee7f6..10342081c 100644 --- a/app/controllers/admin/budget_investments_controller.rb +++ b/app/controllers/admin/budget_investments_controller.rb @@ -1,15 +1,21 @@ class Admin::BudgetInvestmentsController < Admin::BaseController + before_action :load_budget, only: [:index, :show] + has_filters %w{valuation_open without_admin managed valuating valuation_finished all}, only: :index def index - @budget = Budget.includes(:groups).find params[:budget_id] - @investments = @budget.investments.scoped_filter(params, @current_filter).order(cached_votes_up: :desc, created_at: :desc).page(params[:page]) + @investments = Budget::Investment.scoped_filter(params, @current_filter).order(cached_votes_up: :desc, created_at: :desc).page(params[:page]) end def show - @budget = Budget.includes(:groups).find params[:budget_id] - @investment = @budget.investments.find params[:id] + @investment = Budget::Investment.where(budget_id: @budget.id).find params[:id] end + private + + def load_budget + @budget = Budget.includes(:groups).find params[:budget_id] + end + end \ No newline at end of file diff --git a/app/views/admin/budget_investments/_written_by_author.html.erb b/app/views/admin/budget_investments/_written_by_author.html.erb new file mode 100644 index 000000000..b2c46c6f5 --- /dev/null +++ b/app/views/admin/budget_investments/_written_by_author.html.erb @@ -0,0 +1,36 @@ +
+ <%= t "admin.budget_investments.show.info", budget_name: @budget.name, group_name: @investment.group.name, id: @investment.id %> +
+ +
+

<%= @investment.title %>

+ +
+
+

: <%= @investment.group.name %>"> + <%= t("admin.budget_investments.show.heading") %>: + <%= @investment.heading.name %> +

+
+ +
+

+ <%= t("admin.budget_investments.show.by") %>: + <%= link_to @investment.author.name, admin_user_path(@investment.author) %> +

+
+ +
+

+ <%= t("admin.budget_investments.show.sent") %>: + <%= l @investment.created_at, format: :datetime %> +

+
+ +
+ +<% if @investment.external_url.present? %> +

<%= text_with_links @investment.external_url %> 

+<% end %> + +<%= safe_html_with_links @investment.description %> diff --git a/app/views/admin/budget_investments/show.html.erb b/app/views/admin/budget_investments/show.html.erb index a142a388d..47e66bc37 100644 --- a/app/views/admin/budget_investments/show.html.erb +++ b/app/views/admin/budget_investments/show.html.erb @@ -1 +1,49 @@ -<%= @investment.title %> \ No newline at end of file +<%= link_to admin_budget_budget_investments_path(Budget::Investment.filter_params(params)), data: {no_turbolink: true} do %> + <%= t("shared.back") %> +<% end %> + +<%= render 'written_by_author' %> + +<%= link_to t("admin.budget_investments.show.edit"), + admin_budget_budget_investment_path(@budget, @investment, + Budget::Investment.filter_params(params)) %> + +
+ +

<%= t("admin.budget_investments.show.classification") %>

+ +

<%= t("admin.budget_investments.show.assigned_admin") %>: + <%= @investment.administrator.try(:name_and_email) || t("admin.budget_investments.show.undefined") %> +

+ +

+ <%= t("admin.budget_investments.show.tags") %>: + + <%= @investment.tags.pluck(:name).join(', ') %> +

+ +

+ <%= t("admin.budget_investments.show.assigned_valuators") %>: + <% if @investment.valuators.any? %> + <%= @investment.valuators.collect(&:name_and_email).join(', ') %> + <% else %> + <%= t("admin.budget_investments.show.undefined") %> + <% end %> +

+ +

+ <%= link_to t("admin.budget_investments.show.edit_classification"), + edit_admin_spending_proposal_path(@investment, + {anchor: 'classification'}.merge(Budget::Investment.filter_params(params))) %> +

+ +
+ +

<%= t("admin.budget_investments.show.dossier") %>

+ +<%# render 'valuation/budget_investments/written_by_valuators' %> + +

+ <%= link_to t("admin.budget_investments.show.edit_dossier"), edit_valuation_spending_proposal_path(@investment) %> +

+ diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index 4a9abf7d3..9b5e7c19c 100755 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -114,6 +114,21 @@ en: feasible: "Feasible (%{price})" not_feasible: "Not feasible" undefined: "Undefined" + show: + assigned_admin: Assigned administrator + assigned_valuators: Assigned valuators + classification: Clasification + info: "%{budget_name} - Group: %{group_name} - Investment project %{id}" + edit: Edit + edit_classification: Edit classification + by: By + sent: Sent + group: Grupo + heading: Partida + dossier: Dossier + edit_dossier: Edit dossier + tags: Tags + undefined: Undefined comments: index: filter: Filter diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 3600c2448..4f4536950 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -114,6 +114,21 @@ es: feasible: "Viable (%{price})" not_feasible: "Inviable" undefined: "Sin definir" + show: + assigned_admin: Administrador asignado + assigned_valuators: Evaluadores asignados + classification: Clasificación + info: "%{budget_name} - Grupo: %{group_name} - Propuesta de inversión %{id}" + edit: Editar + edit_classification: Editar clasificación + by: Autor + sent: Fecha + group: Grupo + heading: Partida + dossier: Informe + edit_dossier: Editar informe + tags: Etiquetas + undefined: Sin definir comments: index: filter: Filtro From 8890380804cbabd932ccab9ba4db420dee488ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Wed, 7 Sep 2016 14:13:40 +0200 Subject: [PATCH 4/8] adds investment editing to admin --- .../admin/budget_investments_controller.rb | 41 +++- app/helpers/budget_headings_helper.rb | 4 + .../admin/budget_investments/edit.html.erb | 69 ++++++ .../admin/budget_investments/show.html.erb | 6 +- .../_written_by_valuators.html.erb | 53 +++++ config/initializers/acts_as_taggable_on.rb | 4 + config/locales/activerecord.en.yml | 9 + config/locales/activerecord.es.yml | 6 + config/locales/admin.en.yml | 8 + config/locales/admin.es.yml | 8 + config/locales/responders.en.yml | 4 +- config/locales/valuation.en.yml | 23 ++ config/locales/valuation.es.yml | 23 ++ config/routes.rb | 2 +- .../features/admin/budget_investments_spec.rb | 214 +++++++++++++++--- 15 files changed, 431 insertions(+), 43 deletions(-) create mode 100644 app/views/admin/budget_investments/edit.html.erb create mode 100644 app/views/valuation/budget_investments/_written_by_valuators.html.erb diff --git a/app/controllers/admin/budget_investments_controller.rb b/app/controllers/admin/budget_investments_controller.rb index 10342081c..17cc76923 100644 --- a/app/controllers/admin/budget_investments_controller.rb +++ b/app/controllers/admin/budget_investments_controller.rb @@ -1,6 +1,7 @@ class Admin::BudgetInvestmentsController < Admin::BaseController - before_action :load_budget, only: [:index, :show] + before_action :load_budget + before_action :load_investment, only: [:show, :edit, :update] has_filters %w{valuation_open without_admin managed valuating valuation_finished all}, only: :index @@ -9,13 +10,49 @@ class Admin::BudgetInvestmentsController < Admin::BaseController end def show - @investment = Budget::Investment.where(budget_id: @budget.id).find params[:id] + end + + def edit + load_admins + load_valuators + load_tags + end + + def update + if @investment.update(budget_investment_params) + redirect_to admin_budget_budget_investment_path(@budget, @investment, Budget::Investment.filter_params(params)), + notice: t("flash.actions.update.budget_investment") + else + load_admins + load_valuators + load_tags + render :edit + end end private + def budget_investment_params + params.require(:budget_investment).permit(:title, :description, :external_url, :heading_id, :administrator_id, :tag_list, valuator_ids: []) + end + def load_budget @budget = Budget.includes(:groups).find params[:budget_id] end + def load_investment + @investment = Budget::Investment.where(budget_id: @budget.id).find params[:id] + end + + def load_admins + @admins = Administrator.includes(:user).all + end + + def load_valuators + @valuators = Valuator.includes(:user).all.order("description ASC").order("users.email ASC") + end + + def load_tags + @tags = ActsAsTaggableOn::Tag.budget_investment_tags + end end \ No newline at end of file diff --git a/app/helpers/budget_headings_helper.rb b/app/helpers/budget_headings_helper.rb index cb042f796..b9944be9b 100644 --- a/app/helpers/budget_headings_helper.rb +++ b/app/helpers/budget_headings_helper.rb @@ -4,4 +4,8 @@ module BudgetHeadingsHelper budget.headings.map {|heading| [heading.name, heading.id]} end + def budget_scoped_heading_select_options(budget) + budget.headings.includes(:group).order("group_id ASC, budget_headings.name ASC").map {|heading| [heading.group.name + ': ' + heading.name, heading.id]} + end + end \ No newline at end of file diff --git a/app/views/admin/budget_investments/edit.html.erb b/app/views/admin/budget_investments/edit.html.erb new file mode 100644 index 000000000..db57c423b --- /dev/null +++ b/app/views/admin/budget_investments/edit.html.erb @@ -0,0 +1,69 @@ +<%= link_to admin_budget_budget_investment_path(@budget, @investment, Budget::Investment.filter_params(params)), class: 'back' do %> + <%= t("shared.back") %> +<% end %> + +<%= form_for @investment, + url: admin_budget_budget_investment_path(@budget, @investment) do |f| %> + + <% Budget::Investment.filter_params(params).each do |filter_name, filter_value| %> + <%= hidden_field_tag filter_name, filter_value %> + <% end %> + +
+
+ <%= f.text_field :title, maxlength: Budget::Investment.title_max_length %> +
+ +
+ <%= f.cktext_area :description, maxlength: Budget::Investment.description_max_length, ckeditor: { language: I18n.locale } %> +
+ +
+ <%= f.text_field :external_url %> +
+ +
+ <%= f.select :heading_id, budget_scoped_heading_select_options(@budget), include_blank: t("admin.budget_investments.edit.select_heading") %> +
+
+ +

<%= t("admin.budget_investments.edit.classification") %>

+ +
+ +
+ <%= f.select(:administrator_id, + @admins.collect{ |a| [a.name_and_email, a.id ] }, + { include_blank: t("admin.budget_investments.edit.undefined") }) %> +
+ + +
+ <%= f.label :tag_list, t("admin.budget_investments.edit.tags") %> +
+ <% @tags.each do |tag| %> + <%= tag.name %> + <% end %> +
+ <%= f.text_field :tag_list, value: @investment.tag_list.to_s, + label: false, + placeholder: t("admin.budget_investments.edit.tags_placeholder"), + class: 'js-tag-list' %> +
+ +
+ <%= f.label :valuator_ids, t("admin.budget_investments.edit.assigned_valuators") %> + + <%= f.collection_check_boxes :valuator_ids, @valuators, :id, :email do |b| %> + <%= b.label(title: valuator_label(b.object)) { b.check_box + truncate(b.object.description_or_email, length: 60) } %> + <% end %> +
+
+ +

+ <%= f.submit(class: "button", value: t("admin.budget_investments.edit.submit_button")) %> +

+<% end %> + +
+<%# render 'valuation/budget_investments/written_by_valuators' %> \ No newline at end of file diff --git a/app/views/admin/budget_investments/show.html.erb b/app/views/admin/budget_investments/show.html.erb index 47e66bc37..e7423b82d 100644 --- a/app/views/admin/budget_investments/show.html.erb +++ b/app/views/admin/budget_investments/show.html.erb @@ -5,7 +5,7 @@ <%= render 'written_by_author' %> <%= link_to t("admin.budget_investments.show.edit"), - admin_budget_budget_investment_path(@budget, @investment, + edit_admin_budget_budget_investment_path(@budget, @investment, Budget::Investment.filter_params(params)) %>
@@ -33,7 +33,7 @@

<%= link_to t("admin.budget_investments.show.edit_classification"), - edit_admin_spending_proposal_path(@investment, + edit_admin_budget_budget_investment_path(@budget, @investment, {anchor: 'classification'}.merge(Budget::Investment.filter_params(params))) %>

@@ -41,7 +41,7 @@

<%= t("admin.budget_investments.show.dossier") %>

-<%# render 'valuation/budget_investments/written_by_valuators' %> +<%= render 'valuation/budget_investments/written_by_valuators' %>

<%= link_to t("admin.budget_investments.show.edit_dossier"), edit_valuation_spending_proposal_path(@investment) %> diff --git a/app/views/valuation/budget_investments/_written_by_valuators.html.erb b/app/views/valuation/budget_investments/_written_by_valuators.html.erb new file mode 100644 index 000000000..9dc5a8e8c --- /dev/null +++ b/app/views/valuation/budget_investments/_written_by_valuators.html.erb @@ -0,0 +1,53 @@ +

+ + <%= t("valuation.budget_investments.show.price") %> + (<%= t("valuation.budget_investments.show.currency") %>): + + <% if @investment.price.present? %> + <%= @investment.price %> + <% else %> + <%= t("valuation.budget_investments.show.undefined") %> + <% end %> +

+ +

+ + <%= t("valuation.budget_investments.show.price_first_year") %> + (<%= t("valuation.budget_investments.show.currency") %>): + + + <% if @investment.price_first_year.present? %> + <%= @investment.price_first_year %> + <% else %> + <%= t("valuation.budget_investments.show.undefined") %> + <% end %> +

+ +<%= explanation_field @investment.price_explanation %> + +

+ <%= t("valuation.budget_investments.show.duration") %>: + <% if @investment.duration.present? %> + <%= @investment.duration %> + <% else %> + <%= t("valuation.budget_investments.show.undefined") %> + <% end %> +

+ +

+ <%= t("valuation.budget_investments.show.feasibility") %>: + <%= t("valuation.budget_investments.show.#{@investment.feasibility}") %> +

+ +<%= explanation_field @investment.unfeasibility_explanation %> + +<% if @investment.valuation_finished %> +

+ <%= t("valuation.budget_investments.show.valuation_finished") %> +

+<% end %> + +<% if @investment.internal_comments.present? %> +

<%= t("valuation.budget_investments.show.internal_comments") %>

+ <%= explanation_field @investment.internal_comments %> +<% end %> diff --git a/config/initializers/acts_as_taggable_on.rb b/config/initializers/acts_as_taggable_on.rb index 7b534897a..5cf75b8bb 100644 --- a/config/initializers/acts_as_taggable_on.rb +++ b/config/initializers/acts_as_taggable_on.rb @@ -42,6 +42,10 @@ module ActsAsTaggableOn ActsAsTaggableOn::Tag.where('taggings.taggable_type' => 'SpendingProposal').includes(:taggings).order(:name).uniq end + def self.budget_investment_tags + ActsAsTaggableOn::Tag.where('taggings.taggable_type' => 'Budget::Investment').includes(:taggings).order(:name).uniq + end + private def custom_counter_field_name_for(taggable_type) "#{taggable_type.underscore.pluralize}_count" diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index 5a7a591bd..8eb300289 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -7,6 +7,9 @@ en: budget: one: "Participatory budget" other: "Participatory budgets" + budget/investment: + one: "Investment" + other: "Investments" comment: one: "Comment" other: "Comments" @@ -38,6 +41,12 @@ en: one: "Spending proposal" other: "Spending proposals" attributes: + budget/investment: + administrator_id: "Administrator" + description: "Description" + external_url: "Link to additional documentation" + heading_id: "Heading" + title: "Title" comment: body: "Comment" user: "User" diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index c7c811801..50ef09cf4 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -41,6 +41,12 @@ es: one: "Propuesta de inversión" other: "Propuestas de inversión" attributes: + budget/investment: + administrator_id: "Administrador" + description: "Descripción" + external_url: "Enlace a documentación adicional" + heading_id: "Partida presupuestaria" + title: "Título" comment: body: "Comentario" user: "Usuario" diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index 9b5e7c19c..c9bd21c18 100755 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -129,6 +129,14 @@ en: edit_dossier: Edit dossier tags: Tags undefined: Undefined + edit: + classification: Clasification + assigned_valuators: Valuators + select_heading: Select heading + submit_button: Update + tags: Tags + tags_placeholder: "Write the tags you want separated by commas (,)" + undefined: Undefined comments: index: filter: Filter diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 4f4536950..07b0c9991 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -129,6 +129,14 @@ es: edit_dossier: Editar informe tags: Etiquetas undefined: Sin definir + edit: + classification: Clasificación + assigned_valuators: Evaluadores + select_heading: Seleccionar partida + submit_button: Actualizar + tags: Etiquetas + tags_placeholder: "Escribe las etiquetas que desees separadas por comas (,)" + undefined: Sin definir comments: index: filter: Filtro diff --git a/config/locales/responders.en.yml b/config/locales/responders.en.yml index 825a9957b..9e8c8c4a7 100755 --- a/config/locales/responders.en.yml +++ b/config/locales/responders.en.yml @@ -17,7 +17,7 @@ en: debate: "Debate updated successfully." proposal: "Proposal updated successfully." spending_proposal: "Investment project updated succesfully." - budget_investment: "Budget Investment updated succesfully." + budget_investment: "Investment project updated succesfully." destroy: spending_proposal: "Spending proposal deleted succesfully." - budget_investment: "Budget Investment deleted succesfully." + budget_investment: "Investment project deleted succesfully." diff --git a/config/locales/valuation.en.yml b/config/locales/valuation.en.yml index e136562f4..c3b6d8283 100644 --- a/config/locales/valuation.en.yml +++ b/config/locales/valuation.en.yml @@ -4,6 +4,29 @@ en: menu: title: Valuation spending_proposals: Spending proposals + budget_investments: + show: + back: Back + heading: Investment project + info: Author info + by: Sent by + sent: Sent at + heading: Heading + dossier: Dossier + edit_dossier: Edit dossier + price: Price + price_first_year: Cost during the first year + currency: "€" + feasibility: Feasibility + feasible: Feasible + unfeasible: Unfeasible + undefined: Undefined + valuation_finished: Valuation finished + duration: Time scope + internal_comments: Internal comments + responsibles: Responsibles + assigned_admin: Assigned admin + assigned_valuators: Assigned valuators spending_proposals: index: geozone_filter_all: All zones diff --git a/config/locales/valuation.es.yml b/config/locales/valuation.es.yml index 7d4ff5ed6..34771587d 100644 --- a/config/locales/valuation.es.yml +++ b/config/locales/valuation.es.yml @@ -4,6 +4,29 @@ es: menu: title: Evaluación spending_proposals: Propuestas de inversión + budget_investments: + show: + back: Volver + heading: Propuesta de inversión + info: Datos de envío + by: Enviada por + sent: Fecha de creación + heading: Partida + dossier: Informe + edit_dossier: Editar informe + price: Coste + price_first_year: Coste en el primer año + currency: "€" + feasibility: Viabilidad + feasible: Viable + unfeasible: Inviable + undefined: Sin definir + valuation_finished: Informe finalizado + duration: Plazo de ejecución + internal_comments: Comentarios internos + responsibles: Responsables + assigned_admin: Administrador asignado + assigned_valuators: Evaluadores asignados spending_proposals: index: geozone_filter_all: Todos los ámbitos de actuación diff --git a/config/routes.rb b/config/routes.rb index 278153a19..b22806dbd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -162,7 +162,7 @@ Rails.application.routes.draw do end end - resources :budget_investments, only: [:index, :show] + resources :budget_investments, only: [:index, :show, :edit, :update] end resources :banners, only: [:index, :new, :create, :edit, :update, :destroy] do diff --git a/spec/features/admin/budget_investments_spec.rb b/spec/features/admin/budget_investments_spec.rb index ad08ee634..c34354ede 100644 --- a/spec/features/admin/budget_investments_spec.rb +++ b/spec/features/admin/budget_investments_spec.rb @@ -98,15 +98,16 @@ feature 'Admin budget investments' do expect(page).to have_link("Change name") expect(page).to_not have_link("Plant trees") - # click_link("Realocate visitors") - # click_link("Edit classification") - # expect(page).to have_button("Update") - # click_link("Go back") - # expect(page).to_not have_button("Update") - # click_link("Go back") + click_link("Change name") + click_link("Edit classification") + expect(page).to have_button("Update") + click_link("Go back") + expect(page).to_not have_button("Update") + click_link("Go back") - # expect(page).to_not have_link("Destroy the city") - # expect(page).to have_link("Realocate visitors") + expect(page).to_not have_link("Realocate visitors") + expect(page).to have_link("Change name") + expect(page).to_not have_link("Plant trees") end scenario "Filtering by admin", :js do @@ -142,16 +143,16 @@ feature 'Admin budget investments' do expect(page).to_not have_link("Destroy the city") expect(page).to have_link("Realocate visitors") - # click_link("Realocate visitors") - # click_link("Edit classification") - # expect(page).to have_button("Update") - # click_link("Go back") - # expect(page).to_not have_button("Update") - # click_link("Go back") + click_link("Realocate visitors") + click_link("Edit classification") + expect(page).to have_button("Update") + click_link("Go back") + expect(page).to_not have_button("Update") + click_link("Go back") - # expect(page).to have_content('There is 1 investment') - # expect(page).to_not have_link("Destroy the city") - # expect(page).to have_link("Realocate visitors") + expect(page).to have_content('There is 1 investment') + expect(page).to_not have_link("Destroy the city") + expect(page).to have_link("Realocate visitors") end scenario "Filtering by valuator", :js do @@ -188,16 +189,16 @@ feature 'Admin budget investments' do expect(page).to_not have_link("Destroy the city") expect(page).to have_link("Realocate visitors") - # click_link("Realocate visitors") - # click_link("Edit classification") - # expect(page).to have_button("Update") - # click_link("Go back") - # expect(page).to_not have_button("Update") - # click_link("Go back") + click_link("Realocate visitors") + click_link("Edit classification") + expect(page).to have_button("Update") + click_link("Go back") + expect(page).to_not have_button("Update") + click_link("Go back") - # expect(page).to have_content('There is 1 investment') - # expect(page).to_not have_link("Destroy the city") - # expect(page).to have_link("Realocate visitors") + expect(page).to have_content('There is 1 investment') + expect(page).to_not have_link("Destroy the city") + expect(page).to have_link("Realocate visitors") end scenario "Current filter is properly highlighted" do @@ -297,16 +298,159 @@ feature 'Admin budget investments' do expect(page).to have_content("Educate the children") expect(page).to have_content("More schools") - # click_link("Educate the children") - # click_link("Edit classification") - # expect(page).to have_button("Update") - # click_link("Go back") - # expect(page).to_not have_button("Update") - # click_link("Go back") + click_link("Educate the children") + click_link("Edit classification") + expect(page).to have_button("Update") + click_link("Go back") + expect(page).to_not have_button("Update") + click_link("Go back") - # expect(page).to_not have_content("More hospitals") - # expect(page).to have_content("Educate the children") - # expect(page).to have_content("More schools") + expect(page).to_not have_content("More hospitals") + expect(page).to have_content("Educate the children") + expect(page).to have_content("More schools") + end + + end + + scenario 'Show' do + administrator = create(:administrator, user: create(:user, username: 'Ana', email: 'ana@admins.org')) + valuator = create(:valuator, user: create(:user, username: 'Rachel', email: 'rachel@valuators.org')) + budget_investment = create(:budget_investment, + price: 1234, + price_first_year: 1000, + feasibility: "unfeasible", + unfeasibility_explanation: 'It is impossible', + administrator: administrator) + budget_investment.valuators << valuator + + visit admin_budget_budget_investments_path(budget_investment.budget) + + click_link budget_investment.title + + expect(page).to have_content(budget_investment.title) + expect(page).to have_content(budget_investment.description) + expect(page).to have_content(budget_investment.author.name) + expect(page).to have_content(budget_investment.heading.name) + expect(page).to have_content('1234') + expect(page).to have_content('1000') + expect(page).to have_content('Unfeasible') + expect(page).to have_content('It is impossible') + expect(page).to have_content('Ana (ana@admins.org)') + + within('#assigned_valuators') do + expect(page).to have_content('Rachel (rachel@valuators.org)') + end + end + + context "Edit" do + + scenario "Change title, description or heading" do + budget_investment = create(:budget_investment) + create(:budget_heading, group: budget_investment.group, name: "Barbate") + + visit admin_budget_budget_investment_path(budget_investment.budget, budget_investment) + click_link 'Edit' + + fill_in 'budget_investment_title', with: 'Potatoes' + fill_in 'budget_investment_description', with: 'Carrots' + select "#{budget_investment.group.name}: Barbate", from: 'budget_investment[heading_id]' + + click_button 'Update' + + expect(page).to have_content 'Potatoes' + expect(page).to have_content 'Carrots' + expect(page).to have_content 'Barbate' + end + + scenario "Add administrator" do + budget_investment = create(:budget_investment) + administrator = create(:administrator, user: create(:user, username: 'Marta', email: 'marta@admins.org')) + + visit admin_budget_budget_investment_path(budget_investment.budget, budget_investment) + click_link 'Edit classification' + + select 'Marta (marta@admins.org)', from: 'budget_investment[administrator_id]' + click_button 'Update' + + expect(page).to have_content 'Investment project updated succesfully.' + expect(page).to have_content 'Assigned administrator: Marta' + end + + scenario "Add valuators" do + budget_investment = create(:budget_investment) + + valuator1 = create(:valuator, user: create(:user, username: 'Valentina', email: 'v1@valuators.org')) + valuator2 = create(:valuator, user: create(:user, username: 'Valerian', email: 'v2@valuators.org')) + valuator3 = create(:valuator, user: create(:user, username: 'Val', email: 'v3@valuators.org')) + + visit admin_budget_budget_investment_path(budget_investment.budget, budget_investment) + click_link 'Edit classification' + + check "budget_investment_valuator_ids_#{valuator1.id}" + check "budget_investment_valuator_ids_#{valuator3.id}" + + click_button 'Update' + + expect(page).to have_content 'Investment project updated succesfully.' + + within('#assigned_valuators') do + expect(page).to have_content('Valentina (v1@valuators.org)') + expect(page).to have_content('Val (v3@valuators.org)') + expect(page).to_not have_content('Undefined') + expect(page).to_not have_content('Valerian (v2@valuators.org)') + end + end + + scenario "Adds existing tags", :js do + create(:budget_investment, tag_list: 'Education, Health') + + budget_investment = create(:budget_investment) + + visit admin_budget_budget_investment_path(budget_investment.budget, budget_investment) + click_link 'Edit classification' + + find('.js-add-tag-link', text: 'Education').click + + fill_in 'budget_investment_title', with: 'Updated title' + + click_button 'Update' + + expect(page).to have_content 'Investment project updated succesfully.' + + within "#tags" do + expect(page).to have_content 'Education' + expect(page).to_not have_content 'Health' + end + end + + scenario "Adds non existent tags" do + budget_investment = create(:budget_investment) + + visit admin_budget_budget_investment_path(budget_investment.budget, budget_investment) + click_link 'Edit classification' + + fill_in 'budget_investment_tag_list', with: 'Refugees, Solidarity' + click_button 'Update' + + expect(page).to have_content 'Investment project updated succesfully.' + + within "#tags" do + expect(page).to have_content 'Refugees' + expect(page).to have_content 'Solidarity' + end + end + + scenario "Errors on update" do + budget_investment = create(:budget_investment) + + visit admin_budget_budget_investment_path(budget_investment.budget, budget_investment) + click_link 'Edit' + + fill_in 'budget_investment_title', with: '' + + click_button 'Update' + + expect(page).to have_content "can't be blank" end end From f8a5f02cc920c23238f81757751626e93540b690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Wed, 7 Sep 2016 15:08:03 +0200 Subject: [PATCH 5/8] adds i18n to budgets setting --- config/locales/settings.en.yml | 1 + config/locales/settings.es.yml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/config/locales/settings.en.yml b/config/locales/settings.en.yml index 32874e32b..31f0d8add 100755 --- a/config/locales/settings.en.yml +++ b/config/locales/settings.en.yml @@ -14,6 +14,7 @@ en: email_domain_for_officials: "Email domain for public officials" per_page_code: "Code to be included on every page" feature: + budgets: Participatory budgeting debates: Debates spending_proposals: Investment projects spending_proposal_features: diff --git a/config/locales/settings.es.yml b/config/locales/settings.es.yml index aa5dea14e..532fb878e 100644 --- a/config/locales/settings.es.yml +++ b/config/locales/settings.es.yml @@ -14,7 +14,8 @@ es: email_domain_for_officials: "Dominio de email para cargos públicos" per_page_code: "Código a incluir en cada página" feature: + budgets: Presupuestos participativos debates: Debates spending_proposals: Propuestas de inversión spending_proposal_features: - voting_allowed: Votaciones sobre propuestas de inversión. \ No newline at end of file + voting_allowed: Votaciones sobre propuestas de inversión \ No newline at end of file From 2d56415048c4968e2f33e5caea367b91599810c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Wed, 7 Sep 2016 15:13:39 +0200 Subject: [PATCH 6/8] adds feature flag for admin/budgets management --- app/controllers/admin/budget_headings_controller.rb | 2 ++ app/controllers/admin/budget_investments_controller.rb | 2 ++ app/controllers/admin/budgets_controller.rb | 2 ++ app/controllers/budgets_controller.rb | 3 +++ spec/features/admin/budget_investments_spec.rb | 9 +++++++++ 5 files changed, 18 insertions(+) diff --git a/app/controllers/admin/budget_headings_controller.rb b/app/controllers/admin/budget_headings_controller.rb index 3c8ccafa0..56903b744 100644 --- a/app/controllers/admin/budget_headings_controller.rb +++ b/app/controllers/admin/budget_headings_controller.rb @@ -1,4 +1,6 @@ class Admin::BudgetHeadingsController < Admin::BaseController + include FeatureFlags + feature_flag :budgets def create @budget = Budget.find params[:budget_id] diff --git a/app/controllers/admin/budget_investments_controller.rb b/app/controllers/admin/budget_investments_controller.rb index 17cc76923..399ec0ec8 100644 --- a/app/controllers/admin/budget_investments_controller.rb +++ b/app/controllers/admin/budget_investments_controller.rb @@ -1,4 +1,6 @@ class Admin::BudgetInvestmentsController < Admin::BaseController + include FeatureFlags + feature_flag :budgets before_action :load_budget before_action :load_investment, only: [:show, :edit, :update] diff --git a/app/controllers/admin/budgets_controller.rb b/app/controllers/admin/budgets_controller.rb index 144b43a7f..2dd210947 100644 --- a/app/controllers/admin/budgets_controller.rb +++ b/app/controllers/admin/budgets_controller.rb @@ -1,4 +1,6 @@ class Admin::BudgetsController < Admin::BaseController + include FeatureFlags + feature_flag :budgets has_filters %w{open finished}, only: :index diff --git a/app/controllers/budgets_controller.rb b/app/controllers/budgets_controller.rb index db05aafea..9c8a2036b 100644 --- a/app/controllers/budgets_controller.rb +++ b/app/controllers/budgets_controller.rb @@ -1,4 +1,7 @@ class BudgetsController < ApplicationController + include FeatureFlags + feature_flag :budgets + load_and_authorize_resource respond_to :html, :js diff --git a/spec/features/admin/budget_investments_spec.rb b/spec/features/admin/budget_investments_spec.rb index c34354ede..a9ec45dba 100644 --- a/spec/features/admin/budget_investments_spec.rb +++ b/spec/features/admin/budget_investments_spec.rb @@ -9,6 +9,15 @@ feature 'Admin budget investments' do @budget = create(:budget) end + context "Feature flag" do + + scenario 'Disabled with a feature flag' do + Setting['feature.budgets'] = nil + expect{ visit admin_budgets_path }.to raise_exception(FeatureFlags::FeatureDisabled) + end + + end + context "Index" do scenario 'Displaying investmentss' do From 0fc31b125915c84e017aa86a1469f99545be9f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baza=CC=81n?= Date: Thu, 8 Sep 2016 11:44:07 +0200 Subject: [PATCH 7/8] adds budget/budget_investment indexes to valuators --- app/assets/stylesheets/admin.scss | 2 +- .../admin/budget_groups_controller.rb | 2 + .../budget_investments_controller.rb | 81 +++++++++ .../valuation/budgets_controller.rb | 13 ++ .../spending_proposals_controller.rb | 2 +- app/helpers/valuation_helper.rb | 6 +- app/models/abilities/valuator.rb | 2 +- app/models/budget.rb | 6 +- app/views/valuation/_menu.html.erb | 9 + .../budget_investments/index.html.erb | 42 +++++ app/views/valuation/budgets/index.html.erb | 17 ++ config/i18n-tasks.yml | 2 + config/locales/valuation.en.yml | 25 ++- config/locales/valuation.es.yml | 25 ++- config/routes.rb | 8 +- spec/features/admin/budgets_spec.rb | 2 +- .../valuation/budget_investments_spec.rb | 161 ++++++++++++++++++ spec/features/valuation/budgets_spec.rb | 74 ++++++++ 18 files changed, 460 insertions(+), 19 deletions(-) create mode 100644 app/controllers/valuation/budget_investments_controller.rb create mode 100644 app/controllers/valuation/budgets_controller.rb create mode 100644 app/views/valuation/budget_investments/index.html.erb create mode 100644 app/views/valuation/budgets/index.html.erb create mode 100644 spec/features/valuation/budget_investments_spec.rb create mode 100644 spec/features/valuation/budgets_spec.rb diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 196d0be17..7eab8c070 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -384,7 +384,7 @@ body.admin { } } -.admin-content .select-geozone { +.admin-content .select-geozone, .admin-content .select-heading { a { display: block; diff --git a/app/controllers/admin/budget_groups_controller.rb b/app/controllers/admin/budget_groups_controller.rb index 18f5a6b12..9c5a54b98 100644 --- a/app/controllers/admin/budget_groups_controller.rb +++ b/app/controllers/admin/budget_groups_controller.rb @@ -1,4 +1,6 @@ class Admin::BudgetGroupsController < Admin::BaseController + include FeatureFlags + feature_flag :budgets def create @budget = Budget.find params[:budget_id] diff --git a/app/controllers/valuation/budget_investments_controller.rb b/app/controllers/valuation/budget_investments_controller.rb new file mode 100644 index 000000000..348f275da --- /dev/null +++ b/app/controllers/valuation/budget_investments_controller.rb @@ -0,0 +1,81 @@ +class Valuation::BudgetInvestmentsController < Valuation::BaseController + include FeatureFlags + feature_flag :budgets + + before_action :restrict_access_to_assigned_items, only: [:show, :edit, :valuate] + before_action :load_budget + + has_filters %w{valuating valuation_finished}, only: :index + + load_and_authorize_resource :investment, class: "Budget::Investment" + + def index + @heading_filters = heading_filters + if current_user.valuator? && @budget.present? + @investments = @budget.investments.scoped_filter(params_for_current_valuator, @current_filter).order(cached_votes_up: :desc).page(params[:page]) + else + @investments = Budget::Investment.none.page(params[:page]) + end + end + + def valuate + if valid_price_params? && @investment.update(valuation_params) + + if @investment.unfeasible_email_pending? + @investment.send_unfeasible_email + end + + redirect_to valuation_budget_investment_path(@investment), notice: t('valuation.budget_investments.notice.valuate') + else + render action: :edit + end + end + + private + + def load_budget + @budget = Budget.find(params[:budget_id]) + end + + def heading_filters + investments = @budget.investments.by_valuator(current_user.valuator.try(:id)).valuation_open.select(:heading_id).all.to_a + + [ { name: t('valuation.budget_investments.index.headings_filter_all'), + id: nil, + pending_count: investments.size + } + ] + Budget::Heading.where(id: investments.map(&:heading_id).uniq).order(name: :asc).collect do |h| + { name: h.name, + id: h.id, + pending_count: investments.count{|x| x.heading_id == h.id} + } + end + end + + def params_for_current_valuator + Budget::Investment.filter_params(params).merge({valuator_id: current_user.valuator.id, budget_id: @budget.id}) + end + + def valuation_params + params[:budget_investment][:feasible] = nil if params[:budget_investment][:feasible] == 'nil' + + params.require(:budget_investment).permit(:price, :price_first_year, :price_explanation, :feasible, :feasible_explanation, :duration, :valuation_finished, :internal_comments) + end + + def restrict_access_to_assigned_items + raise ActionController::RoutingError.new('Not Found') unless current_user.administrator? || ValuatorAssignment.exists?(investment_id: params[:id], valuator_id: current_user.valuator.id) + end + + def valid_price_params? + if /\D/.match params[:budget_investment][:price] + @investment.errors.add(:price, I18n.t('budget.investments.wrong_price_format')) + end + + if /\D/.match params[:budget_investment][:price_first_year] + @investment.errors.add(:price_first_year, I18n.t('budget.investments.wrong_price_format')) + end + + @investment.errors.empty? + end + +end \ No newline at end of file diff --git a/app/controllers/valuation/budgets_controller.rb b/app/controllers/valuation/budgets_controller.rb new file mode 100644 index 000000000..e96d018dd --- /dev/null +++ b/app/controllers/valuation/budgets_controller.rb @@ -0,0 +1,13 @@ +class Valuation::BudgetsController < Valuation::BaseController + include FeatureFlags + feature_flag :budgets + + has_filters %w{open finished}, only: :index + + load_and_authorize_resource + + def index + @budgets = Budget.send(@current_filter).order(created_at: :desc).page(params[:page]) + end + +end \ No newline at end of file diff --git a/app/controllers/valuation/spending_proposals_controller.rb b/app/controllers/valuation/spending_proposals_controller.rb index d3bc585b1..f130baec4 100644 --- a/app/controllers/valuation/spending_proposals_controller.rb +++ b/app/controllers/valuation/spending_proposals_controller.rb @@ -58,7 +58,7 @@ class Valuation::SpendingProposalsController < Valuation::BaseController end def params_for_current_valuator - params.merge({valuator_id: current_user.valuator.id}) + params.merge({valuator_id: current_user.valuator.id}) end def restrict_access_to_assigned_items diff --git a/app/helpers/valuation_helper.rb b/app/helpers/valuation_helper.rb index 0983ea567..ded5fa0ed 100644 --- a/app/helpers/valuation_helper.rb +++ b/app/helpers/valuation_helper.rb @@ -11,14 +11,14 @@ module ValuationHelper def assigned_valuators_info(valuators) case valuators.size when 0 - t("valuation.spending_proposals.index.no_valuators_assigned") + t("valuation.budget_investments.index.no_valuators_assigned") when 1 - "".html_safe + + "".html_safe + valuators.first.name + "".html_safe else "".html_safe + - t('valuation.spending_proposals.index.valuators_assigned', count: valuators.size) + + t('valuation.budget_investments.index.valuators_assigned', count: valuators.size) + "".html_safe end end diff --git a/app/models/abilities/valuator.rb b/app/models/abilities/valuator.rb index 85c598d12..d3979a640 100644 --- a/app/models/abilities/valuator.rb +++ b/app/models/abilities/valuator.rb @@ -5,7 +5,7 @@ module Abilities def initialize(user) valuator = user.valuator can [:read, :update, :valuate], SpendingProposal - can [:update, :valuate], Budget::Investment, id: valuator.investment_ids, budget: { valuating: true } + can [:read, :update, :valuate], Budget::Investment, id: valuator.investment_ids, budget: { valuating: true } end end end diff --git a/app/models/budget.rb b/app/models/budget.rb index b8fd34da9..e716c0181 100644 --- a/app/models/budget.rb +++ b/app/models/budget.rb @@ -13,10 +13,10 @@ class Budget < ActiveRecord::Base has_many :ballots, dependent: :destroy has_many :groups, dependent: :destroy has_many :headings, through: :groups - has_many :investments, through: :headings - scope :open, -> { where.not(phase: "finished") } - scope :finished, -> { where(phase: "finished") } + scope :open, -> { where.not(phase: "finished") } + scope :finished, -> { where(phase: "finished") } + scope :valuating, -> { where(valuating: true) } def on_hold? phase == "on_hold" diff --git a/app/views/valuation/_menu.html.erb b/app/views/valuation/_menu.html.erb index c63b6421a..3908b9279 100644 --- a/app/views/valuation/_menu.html.erb +++ b/app/views/valuation/_menu.html.erb @@ -13,5 +13,14 @@ <% end %> + <% if feature?(:budgets) %> +
  • > + <%= link_to valuation_budgets_path do %> + + <%= t("valuation.menu.budgets") %> + <% end %> +
  • + <% end %> + diff --git a/app/views/valuation/budget_investments/index.html.erb b/app/views/valuation/budget_investments/index.html.erb new file mode 100644 index 000000000..9b82f7f5a --- /dev/null +++ b/app/views/valuation/budget_investments/index.html.erb @@ -0,0 +1,42 @@ +

    <%= @budget.name %> - <%= t("valuation.budget_investments.index.title") %>

    + +
    + <% @heading_filters.each_slice(8) do |slice| %> +
    + <% slice.each do |filter| %> + <%= link_to valuation_budget_budget_investments_path(budget_id: @budget.id, heading_id: filter[:id]), + class: "#{'active' if params[:heading_id].to_s == filter[:id].to_s}" do %> + <%= filter[:name] %> (<%= filter[:pending_count] %>) + <% end %> + <% end %> +
    + <% end %> +
    + +<%= render 'shared/filter_subnav', i18n_namespace: "valuation.budget_investments.index" %> + +

    <%= page_entries_info @investments %>

    + + + <% @investments.each do |investment| %> + + + + + + + + <% end %> +
    + <%= investment.id %> + + <%= link_to investment.title, valuation_budget_budget_investment_path(@budget, investment) %> + + <%= link_to t("valuation.budget_investments.index.edit"), edit_valuation_budget_budget_investment_path(@budget, investment) %> + + <%= assigned_valuators_info(investment.valuators) %> + + <%= investment.heading.name %> +
    + +<%= paginate @investments %> diff --git a/app/views/valuation/budgets/index.html.erb b/app/views/valuation/budgets/index.html.erb new file mode 100644 index 000000000..014633373 --- /dev/null +++ b/app/views/valuation/budgets/index.html.erb @@ -0,0 +1,17 @@ +

    <%= t("valuation.budgets.index.title") %>

    + +<%= render 'shared/filter_subnav', i18n_namespace: "valuation.budgets.index" %> + +

    <%= page_entries_info @budgets %>

    + + + <% @budgets.each do |budget| %> + + + + <% end %> +
    + <%= link_to budget.name, valuation_budget_budget_investments_path(budget_id: budget.id) %> +
    + +<%= paginate @budgets %> \ No newline at end of file diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 161ed6dfd..9b675ffd0 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -132,6 +132,8 @@ ignore_unused: - 'moderation.debates.index.filter*' - 'moderation.debates.index.order*' - 'valuation.spending_proposals.index.filter*' + - 'valuation.budgets.index.filter*' + - 'valuation.budget_investments.index.filter*' - 'users.show.filters.*' - 'debates.index.select_order' - 'debates.index.orders.*' diff --git a/config/locales/valuation.en.yml b/config/locales/valuation.en.yml index c3b6d8283..b1cf1ff81 100644 --- a/config/locales/valuation.en.yml +++ b/config/locales/valuation.en.yml @@ -3,8 +3,27 @@ en: valuation: menu: title: Valuation + budgets: Participatory budgets spending_proposals: Spending proposals + budgets: + index: + title: Participatory budgets + filters: + open: Open + finished: Finished budget_investments: + index: + headings_filter_all: All headings + filters: + valuation_open: Open + valuating: Under valuation + valuation_finished: Valuation finished + title: Investment projects + edit: Edit + valuators_assigned: + one: Assigned valuator + other: "%{count} valuators assigned" + no_valuators_assigned: No valuators assigned show: back: Back heading: Investment project @@ -27,6 +46,8 @@ en: responsibles: Responsibles assigned_admin: Assigned admin assigned_valuators: Assigned valuators + notice: + valuate: "Dossier updated" spending_proposals: index: geozone_filter_all: All zones @@ -36,10 +57,6 @@ en: valuation_finished: Valuation finished title: Investment projects for participatory budgeting edit: Edit - valuators_assigned: - one: Assigned valuator - other: "%{count} valuators assigned" - no_valuators_assigned: No valuators assigned show: back: Back heading: Investment project diff --git a/config/locales/valuation.es.yml b/config/locales/valuation.es.yml index 34771587d..163ee658d 100644 --- a/config/locales/valuation.es.yml +++ b/config/locales/valuation.es.yml @@ -3,8 +3,27 @@ es: valuation: menu: title: Evaluación + budgets: Presupuestos participativos spending_proposals: Propuestas de inversión + budgets: + index: + title: Presupuestos participativos + filters: + open: Abiertos + finished: Terminados budget_investments: + index: + headings_filter_all: Todas las partidas + filters: + valuation_open: Abiertas + valuating: En evaluación + valuation_finished: Evaluación finalizada + title: Propuestas de inversión + edit: Editar + valuators_assigned: + one: Evaluador asignado + other: "%{count} evaluadores asignados" + no_valuators_assigned: Sin evaluador show: back: Volver heading: Propuesta de inversión @@ -27,6 +46,8 @@ es: responsibles: Responsables assigned_admin: Administrador asignado assigned_valuators: Evaluadores asignados + notice: + valuate: "Dossier actualizado" spending_proposals: index: geozone_filter_all: Todos los ámbitos de actuación @@ -36,10 +57,6 @@ es: valuation_finished: Evaluación finalizada title: Propuestas de inversión para presupuestos participativos edit: Editar - valuators_assigned: - one: Evaluador asignado - other: "%{count} evaluadores asignados" - no_valuators_assigned: Sin evaluador show: back: Volver heading: Propuesta de inversión diff --git a/config/routes.rb b/config/routes.rb index b22806dbd..c10b4ba2b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -237,11 +237,17 @@ Rails.application.routes.draw do end namespace :valuation do - root to: "spending_proposals#index" + root to: "budgets#index" resources :spending_proposals, only: [:index, :show, :edit] do patch :valuate, on: :member end + + resources :budgets, only: :index do + resources :budget_investments, only: [:index, :show, :edit] do + patch :valuate, on: :member + end + end end namespace :management do diff --git a/spec/features/admin/budgets_spec.rb b/spec/features/admin/budgets_spec.rb index a87cd741a..b01c798cc 100644 --- a/spec/features/admin/budgets_spec.rb +++ b/spec/features/admin/budgets_spec.rb @@ -9,7 +9,7 @@ feature 'Admin budgets' do context 'Feature flag' do - xscenario 'Disabled with a feature flag' do + scenario 'Disabled with a feature flag' do Setting['feature.budgets'] = nil expect{ visit admin_budgets_path }.to raise_exception(FeatureFlags::FeatureDisabled) end diff --git a/spec/features/valuation/budget_investments_spec.rb b/spec/features/valuation/budget_investments_spec.rb new file mode 100644 index 000000000..53f60e416 --- /dev/null +++ b/spec/features/valuation/budget_investments_spec.rb @@ -0,0 +1,161 @@ +require 'rails_helper' + +feature 'Valuation budget investments' do + + background do + @valuator = create(:valuator, user: create(:user, username: 'Rachel', email: 'rachel@valuators.org')) + login_as(@valuator.user) + @budget = create(:budget, valuating: true) + end + + scenario 'Disabled with a feature flag' do + Setting['feature.budgets'] = nil + expect{ visit valuation_budget_budget_investments_path(create(:budget)) }.to raise_exception(FeatureFlags::FeatureDisabled) + end + + scenario 'Index shows budget investments assigned to current valuator' do + investment1 = create(:budget_investment, budget: @budget) + investment2 = create(:budget_investment, budget: @budget) + + investment1.valuators << @valuator + + visit valuation_budget_budget_investments_path(@budget) + + expect(page).to have_content(investment1.title) + expect(page).to_not have_content(investment2.title) + end + + scenario 'Index shows no budget investment to admins no valuators' do + investment1 = create(:budget_investment, budget: @budget) + investment2 = create(:budget_investment, budget: @budget) + + investment1.valuators << @valuator + + logout + login_as create(:administrator).user + visit valuation_budget_budget_investments_path(@budget) + + expect(page).to_not have_content(investment1.title) + expect(page).to_not have_content(investment2.title) + end + + scenario 'Index orders budget investments by votes' do + investment10 = create(:budget_investment, budget: @budget, cached_votes_up: 10) + investment100 = create(:budget_investment, budget: @budget, cached_votes_up: 100) + investment1 = create(:budget_investment, budget: @budget, cached_votes_up: 1) + + investment1.valuators << @valuator + investment10.valuators << @valuator + investment100.valuators << @valuator + + visit valuation_budget_budget_investments_path(@budget) + + expect(investment100.title).to appear_before(investment10.title) + expect(investment10.title).to appear_before(investment1.title) + end + + scenario 'Index shows assignments info' do + investment1 = create(:budget_investment, budget: @budget) + investment2 = create(:budget_investment, budget: @budget) + investment3 = create(:budget_investment, budget: @budget) + + valuator1 = create(:valuator, user: create(:user)) + valuator2 = create(:valuator, user: create(:user)) + valuator3 = create(:valuator, user: create(:user)) + + investment1.valuator_ids = [@valuator.id] + investment2.valuator_ids = [@valuator.id, valuator1.id, valuator2.id] + investment3.valuator_ids = [@valuator.id, valuator3.id] + + visit valuation_budget_budget_investments_path(@budget) + + within("#budget_investment_#{investment1.id}") do + expect(page).to have_content("Rachel") + end + + within("#budget_investment_#{investment2.id}") do + expect(page).to have_content("3 valuators assigned") + end + + within("#budget_investment_#{investment3.id}") do + expect(page).to have_content("2 valuators assigned") + end + end + + scenario "Index filtering by heading", :js do + group = create(:budget_group, budget: @budget) + heading1 = create(:budget_heading, name: "District 9", group: group) + heading2 = create(:budget_heading, name: "Down to the river", group: group) + investment1 = create(:budget_investment, title: "Realocate visitors", heading: heading1, group: group, budget: @budget) + investment2 = create(:budget_investment, title: "Destroy the city", heading: heading2, group: group, budget: @budget) + investment1.valuators << @valuator + investment2.valuators << @valuator + + visit valuation_budget_budget_investments_path(@budget) + + expect(page).to have_link("Realocate visitors") + expect(page).to have_link("Destroy the city") + + + expect(page).to have_content "All headings (2)" + expect(page).to have_content "District 9 (1)" + expect(page).to have_content "Down to the river (1)" + + click_link "District 9", exact: false + + expect(page).to have_link("Realocate visitors") + expect(page).to_not have_link("Destroy the city") + + click_link "Down to the river", exact: false + + expect(page).to have_link("Destroy the city") + expect(page).to_not have_link("Realocate visitors") + + click_link "All headings", exact: false + expect(page).to have_link("Realocate visitors") + expect(page).to have_link("Destroy the city") + end + + scenario "Current filter is properly highlighted" do + filters_links = {'valuating' => 'Under valuation', + 'valuation_finished' => 'Valuation finished'} + + visit valuation_budget_budget_investments_path(@budget) + + expect(page).to_not have_link(filters_links.values.first) + filters_links.keys.drop(1).each { |filter| expect(page).to have_link(filters_links[filter]) } + + filters_links.each_pair do |current_filter, link| + visit valuation_budget_budget_investments_path(@budget, filter: current_filter) + + expect(page).to_not have_link(link) + + (filters_links.keys - [current_filter]).each do |filter| + expect(page).to have_link(filters_links[filter]) + end + end + end + + scenario "Index filtering by valuation status" do + valuating = create(:budget_investment, budget: @budget, title: "Ongoing valuation") + valuated = create(:budget_investment, budget: @budget, title: "Old idea", valuation_finished: true) + valuating.valuators << @valuator + valuated.valuators << @valuator + + visit valuation_budget_budget_investments_path(@budget) + + expect(page).to have_content("Ongoing valuation") + expect(page).to_not have_content("Old idea") + + visit valuation_budget_budget_investments_path(@budget, filter: 'valuating') + + expect(page).to have_content("Ongoing valuation") + expect(page).to_not have_content("Old idea") + + visit valuation_budget_budget_investments_path(@budget, filter: 'valuation_finished') + + expect(page).to_not have_content("Ongoing valuation") + expect(page).to have_content("Old idea") + end + +end \ No newline at end of file diff --git a/spec/features/valuation/budgets_spec.rb b/spec/features/valuation/budgets_spec.rb new file mode 100644 index 000000000..0f61b8078 --- /dev/null +++ b/spec/features/valuation/budgets_spec.rb @@ -0,0 +1,74 @@ +require 'rails_helper' + +feature 'Valuation budgets' do + + background do + @valuator = create(:valuator, user: create(:user, username: 'Rachel', email: 'rachel@valuators.org')) + login_as(@valuator.user) + end + + scenario 'Disabled with a feature flag' do + Setting['feature.budgets'] = nil + expect{ visit valuation_budgets_path }.to raise_exception(FeatureFlags::FeatureDisabled) + end + + context 'Index' do + + scenario 'Displaying budgets' do + budget = create(:budget) + visit valuation_budgets_path + + expect(page).to have_content(budget.name) + end + + scenario 'Filters by phase' do + budget1 = create(:budget) + budget2 = create(:budget, :accepting) + budget3 = create(:budget, :selecting) + budget4 = create(:budget, :balloting) + budget5 = create(:budget, :finished) + + visit valuation_budgets_path + expect(page).to have_content(budget1.name) + expect(page).to have_content(budget2.name) + expect(page).to have_content(budget3.name) + expect(page).to have_content(budget4.name) + expect(page).to_not have_content(budget5.name) + + click_link 'Finished' + expect(page).to_not have_content(budget1.name) + expect(page).to_not have_content(budget2.name) + expect(page).to_not have_content(budget3.name) + expect(page).to_not have_content(budget4.name) + expect(page).to have_content(budget5.name) + + click_link 'Open' + expect(page).to have_content(budget1.name) + expect(page).to have_content(budget2.name) + expect(page).to have_content(budget3.name) + expect(page).to have_content(budget4.name) + expect(page).to_not have_content(budget5.name) + end + + scenario 'Current filter is properly highlighted' do + filters_links = {'open' => 'Open', 'finished' => 'Finished'} + + visit valuation_budgets_path + + expect(page).to_not have_link(filters_links.values.first) + filters_links.keys.drop(1).each { |filter| expect(page).to have_link(filters_links[filter]) } + + filters_links.each_pair do |current_filter, link| + visit valuation_budgets_path(filter: current_filter) + + expect(page).to_not have_link(link) + + (filters_links.keys - [current_filter]).each do |filter| + expect(page).to have_link(filters_links[filter]) + end + end + end + + end + +end \ No newline at end of file From 42c705e1e3b40073b25bdc5ff09a949aeecf2da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Fri, 9 Sep 2016 15:09:06 +0200 Subject: [PATCH 8/8] adds dossier editing of investments to valuation --- app/assets/javascripts/application.js | 2 + ...valuation_budget_investment_form.js.coffee | 32 +++ .../budget_investments_controller.rb | 18 +- .../admin/budget_investments/show.html.erb | 2 +- .../budget_investments/edit.html.erb | 141 +++++++++++ .../budget_investments/show.html.erb | 55 ++++ config/locales/valuation.en.yml | 16 +- config/locales/valuation.es.yml | 16 +- .../valuation/budget_investments_spec.rb | 234 ++++++++++++++++++ 9 files changed, 503 insertions(+), 13 deletions(-) create mode 100644 app/assets/javascripts/valuation_budget_investment_form.js.coffee create mode 100644 app/views/valuation/budget_investments/edit.html.erb create mode 100644 app/views/valuation/budget_investments/show.html.erb diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 7fb92b5e3..b9a3ec9dc 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -42,6 +42,7 @@ //= require suggest //= require forms //= require tracks +//= require valuation_budget_investment_form //= require valuation_spending_proposal_form //= require embed_video //= require banners @@ -63,6 +64,7 @@ var initialize_modules = function() { App.Suggest.initialize(); App.Forms.initialize(); App.Tracks.initialize(); + App.ValuationBudgetInvestmentForm.initialize(); App.ValuationSpendingProposalForm.initialize(); App.EmbedVideo.initialize(); App.Banners.initialize(); diff --git a/app/assets/javascripts/valuation_budget_investment_form.js.coffee b/app/assets/javascripts/valuation_budget_investment_form.js.coffee new file mode 100644 index 000000000..d79ff600e --- /dev/null +++ b/app/assets/javascripts/valuation_budget_investment_form.js.coffee @@ -0,0 +1,32 @@ +App.ValuationBudgetInvestmentForm = + + showFeasibleFields: -> + $('#valuation_budget_investment_edit_form #unfeasible_fields').hide('down') + $('#valuation_budget_investment_edit_form #feasible_fields').show() + + showNotFeasibleFields: -> + $('#valuation_budget_investment_edit_form #feasible_fields').hide('down') + $('#valuation_budget_investment_edit_form #unfeasible_fields').show() + + showAllFields: -> + $('#valuation_budget_investment_edit_form #feasible_fields').show('down') + $('#valuation_budget_investment_edit_form #unfeasible_fields').show('down') + + showFeasibilityFields: -> + feasibility = $("#valuation_budget_investment_edit_form input[type=radio][name='budget_investment[feasibility]']:checked").val() + if feasibility == 'feasible' + App.ValuationBudgetInvestmentForm.showFeasibleFields() + else if feasibility == 'unfeasible' + App.ValuationBudgetInvestmentForm.showNotFeasibleFields() + + + showFeasibilityFieldsOnChange: -> + $("#valuation_budget_investment_edit_form input[type=radio][name='budget_investment[feasibility]']").change -> + App.ValuationBudgetInvestmentForm.showAllFields() + App.ValuationBudgetInvestmentForm.showFeasibilityFields() + + + initialize: -> + App.ValuationBudgetInvestmentForm.showFeasibilityFields() + App.ValuationBudgetInvestmentForm.showFeasibilityFieldsOnChange() + false \ No newline at end of file diff --git a/app/controllers/valuation/budget_investments_controller.rb b/app/controllers/valuation/budget_investments_controller.rb index 348f275da..69269f6bd 100644 --- a/app/controllers/valuation/budget_investments_controller.rb +++ b/app/controllers/valuation/budget_investments_controller.rb @@ -4,6 +4,7 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController before_action :restrict_access_to_assigned_items, only: [:show, :edit, :valuate] before_action :load_budget + before_action :load_investment, only: [:show, :edit, :valuate] has_filters %w{valuating valuation_finished}, only: :index @@ -20,12 +21,7 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController def valuate if valid_price_params? && @investment.update(valuation_params) - - if @investment.unfeasible_email_pending? - @investment.send_unfeasible_email - end - - redirect_to valuation_budget_investment_path(@investment), notice: t('valuation.budget_investments.notice.valuate') + redirect_to valuation_budget_budget_investment_path(@budget, @investment), notice: t('valuation.budget_investments.notice.valuate') else render action: :edit end @@ -37,6 +33,10 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController @budget = Budget.find(params[:budget_id]) end + def load_investment + @investment = @budget.investments.find params[:id] + end + def heading_filters investments = @budget.investments.by_valuator(current_user.valuator.try(:id)).valuation_open.select(:heading_id).all.to_a @@ -57,13 +57,11 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController end def valuation_params - params[:budget_investment][:feasible] = nil if params[:budget_investment][:feasible] == 'nil' - - params.require(:budget_investment).permit(:price, :price_first_year, :price_explanation, :feasible, :feasible_explanation, :duration, :valuation_finished, :internal_comments) + params.require(:budget_investment).permit(:price, :price_first_year, :price_explanation, :feasibility, :unfeasibility_explanation, :duration, :valuation_finished, :internal_comments) end def restrict_access_to_assigned_items - raise ActionController::RoutingError.new('Not Found') unless current_user.administrator? || ValuatorAssignment.exists?(investment_id: params[:id], valuator_id: current_user.valuator.id) + raise ActionController::RoutingError.new('Not Found') unless current_user.administrator? || Budget::ValuatorAssignment.exists?(investment_id: params[:id], valuator_id: current_user.valuator.id) end def valid_price_params? diff --git a/app/views/admin/budget_investments/show.html.erb b/app/views/admin/budget_investments/show.html.erb index e7423b82d..3ebfd4940 100644 --- a/app/views/admin/budget_investments/show.html.erb +++ b/app/views/admin/budget_investments/show.html.erb @@ -44,6 +44,6 @@ <%= render 'valuation/budget_investments/written_by_valuators' %>

    - <%= link_to t("admin.budget_investments.show.edit_dossier"), edit_valuation_spending_proposal_path(@investment) %> + <%= link_to t("admin.budget_investments.show.edit_dossier"), edit_valuation_budget_budget_investment_path(@budget, @investment) %>

    diff --git a/app/views/valuation/budget_investments/edit.html.erb b/app/views/valuation/budget_investments/edit.html.erb new file mode 100644 index 000000000..e03d850a4 --- /dev/null +++ b/app/views/valuation/budget_investments/edit.html.erb @@ -0,0 +1,141 @@ +<%= link_to "#{t('valuation.budget_investments.show.title')} #{@investment.id}", valuation_budget_budget_investment_path(@budget, @investment), class: 'back' %> +

    <%= t("valuation.budget_investments.edit.dossier") %>

    + +<%= form_for(@investment, url: valuate_valuation_budget_budget_investment_path(@budget, @investment), html: {id: "valuation_budget_investment_edit_form"}) do |f| %> + <%= render 'shared/errors', resource: @investment %> +
    +
    +
    + <%= t('valuation.budget_investments.edit.feasibility') %> +
    + + <%= f.radio_button :feasibility, 'undecided', label: false %> + <%= f.label :feasibility_undecided, t('valuation.budget_investments.edit.undefined_feasible') %> + +
    + +
    + + <%= f.radio_button :feasibility, 'feasible', label: false %> + <%= f.label :feasibility_feasible, t('valuation.budget_investments.edit.feasible') %> + +
    + +
    + + <%= f.radio_button :feasibility, 'unfeasible', label: false %> + <%= f.label :feasibility_unfeasible, t('valuation.budget_investments.edit.unfeasible') %> + +
    +
    +
    +
    + +
    + +
    +
    + <%= f.label :unfeasibility_explanation, t("valuation.budget_investments.edit.feasible_explanation_html") %> + <%= f.text_area :unfeasibility_explanation, label: false, rows: 3 %> +
    +
    + +
    + +
    + +
    +
    + <%= f.label :price, "#{t('valuation.budget_investments.edit.price_html', currency: @budget.currency_symbol)}" %> + <%= f.number_field :price, label: false, max: 1000000000000000 %> +
    + +
    + <%= f.label :price_first_year, "#{t('valuation.budget_investments.edit.price_first_year_html', currency: @budget.currency_symbol)}" %> + <%= f.number_field :price_first_year, label: false, max: 1000000000000000 %> +
    +
    + +
    +
    + <%= f.label :price_explanation, t("valuation.budget_investments.edit.price_explanation_html") %> + <%= f.text_area :price_explanation, label: false, rows: 3 %> +
    +
    + +
    +
    + <%= f.label :duration, t("valuation.budget_investments.edit.duration_html") %> + <%= f.text_field :duration, label: false %> +
    +
    + +
    + +
    +
    + <%= f.label :valuation_finished do %> + <%= f.check_box :valuation_finished, title: t('valuation.budget_investments.edit.valuation_finished'), label: false %> + <%= t("valuation.budget_investments.edit.valuation_finished") %> + <% end %> +
    +
    + +
    +
    + <%= f.label :internal_comments, t("valuation.budget_investments.edit.internal_comments_html") %> + <%= f.text_area :internal_comments, label: false, rows: 3 %> +
    +
    + +
    +
    + <%= f.submit(class: "button expanded large", value: t("valuation.budget_investments.edit.save")) %> +
    +
    +<% end %> + +

    <%= @investment.title %>

    + +<%= safe_html_with_links @investment.description %> + +<% if @investment.external_url.present? %> +

    <%= text_with_links @investment.external_url %>

    +<% end %> + +

    <%= t("valuation.budget_investments.show.info") %>

    + +

    <%= t("valuation.budget_investments.show.by") %>: + <%= link_to @investment.author.name, user_path(@investment.author) %> +

    + +

    <%= t("valuation.budget_investments.show.heading") %>: + <%= @investment.heading.name %> +

    + +

    <%= t("valuation.budget_investments.show.sent") %>: + <%= l @investment.created_at, format: :datetime %> +

    + +

    <%= t("valuation.budget_investments.show.responsibles") %>

    + +

    <%= t("valuation.budget_investments.show.assigned_admin") %>: + <% if @investment.administrator.present? %> + <%= @investment.administrator.name %> (<%= @investment.administrator.email %>) + <% else %> + <%= t("valuation.budget_investments.show.undefined") %> + <% end %> +

    + +

    <%= t("valuation.budget_investments.show.assigned_valuators") %>:

    +
    +
      + <% @investment.valuators.each do |valuator| %> +
    • <%= valuator.name %> (<%= valuator.email %>)
    • + <% end %> + + <% if @investment.valuators.empty? %> +
    • <%= t("valuation.budget_investments.show.undefined") %>
    • + <% end %> +
    +
    \ No newline at end of file diff --git a/app/views/valuation/budget_investments/show.html.erb b/app/views/valuation/budget_investments/show.html.erb new file mode 100644 index 000000000..602c53dda --- /dev/null +++ b/app/views/valuation/budget_investments/show.html.erb @@ -0,0 +1,55 @@ +<%= render "shared/back_link" %> + +

    <%= t("valuation.budget_investments.show.title") %> <%= @investment.id %>

    +

    <%= @investment.title %>

    + +<%= safe_html_with_links @investment.description %> + +<% if @investment.external_url.present? %> +

    <%= text_with_links @investment.external_url %>

    +<% end %> + +

    <%= t("valuation.budget_investments.show.info") %>

    + +

    <%= t("valuation.budget_investments.show.by") %>: + <%= link_to @investment.author.name, user_path(@investment.author) %> +

    + +

    <%= t("valuation.budget_investments.show.heading") %>: + <%= @investment.heading.name %> +

    + +

    <%= t("valuation.budget_investments.show.sent") %>: + <%= l @investment.created_at, format: :datetime %> +

    + +

    <%= t("valuation.budget_investments.show.responsibles") %>

    + +

    <%= t("valuation.budget_investments.show.assigned_admin") %>: + <% if @investment.administrator.present? %> + <%= @investment.administrator.name_and_email %> + <% else %> + <%= t("valuation.budget_investments.show.undefined") %> + <% end %> +

    + +

    <%= t("valuation.budget_investments.show.assigned_valuators") %>:

    +
    +
      + <% @investment.valuators.each do |valuator| %> +
    • <%= valuator.name_and_email %>
    • + <% end %> + + <% if @investment.valuators.empty? %> +
    • <%= t("valuation.budget_investments.show.undefined") %>
    • + <% end %> +
    +
    + +

    <%= t("valuation.budget_investments.show.dossier") %>

    + +

    + <%= link_to t("valuation.budget_investments.show.edit_dossier"), edit_valuation_budget_budget_investment_path(@budget, @investment) %> +

    + +<%= render 'written_by_valuators' %> \ No newline at end of file diff --git a/config/locales/valuation.en.yml b/config/locales/valuation.en.yml index b1cf1ff81..a5195deca 100644 --- a/config/locales/valuation.en.yml +++ b/config/locales/valuation.en.yml @@ -26,7 +26,7 @@ en: no_valuators_assigned: No valuators assigned show: back: Back - heading: Investment project + title: Investment project info: Author info by: Sent by sent: Sent at @@ -46,6 +46,20 @@ en: responsibles: Responsibles assigned_admin: Assigned admin assigned_valuators: Assigned valuators + edit: + dossier: Dossier + price_html: "Price (%{currency})" + price_first_year_html: "Cost during the first year (%{currency})" + price_explanation_html: Price explanation + feasibility: Feasibility + feasible: Feasible + unfeasible: Not feasible + undefined_feasible: Pending + feasible_explanation_html: Feasibility explanation + valuation_finished: Valuation finished + duration_html: Time scope + internal_comments_html: Internal comments + save: Save changes notice: valuate: "Dossier updated" spending_proposals: diff --git a/config/locales/valuation.es.yml b/config/locales/valuation.es.yml index 163ee658d..67302437b 100644 --- a/config/locales/valuation.es.yml +++ b/config/locales/valuation.es.yml @@ -26,7 +26,7 @@ es: no_valuators_assigned: Sin evaluador show: back: Volver - heading: Propuesta de inversión + title: Propuesta de inversión info: Datos de envío by: Enviada por sent: Fecha de creación @@ -46,6 +46,20 @@ es: responsibles: Responsables assigned_admin: Administrador asignado assigned_valuators: Evaluadores asignados + edit: + dossier: Informe + price_html: "Coste (%{currency}) (dato público)" + price_first_year_html: "Coste en el primer año (%{currency}) (opcional, privado)" + price_explanation_html: "Informe de coste (opcional, dato público)" + feasibility: Viabilidad + feasible: Viable + unfeasible: Inviable + undefined_feasible: Sin decidir + feasible_explanation_html: "Informe de inviabilidad (en caso de que lo sea, dato público)" + valuation_finished: Informe finalizado + duration_html: "Plazo de ejecución (opcional, dato no público)" + internal_comments_html: "Comentarios y observaciones (para responsables internos, dato no público)" + save: Guardar cambios notice: valuate: "Dossier actualizado" spending_proposals: diff --git a/spec/features/valuation/budget_investments_spec.rb b/spec/features/valuation/budget_investments_spec.rb index 53f60e416..56a20f2c6 100644 --- a/spec/features/valuation/budget_investments_spec.rb +++ b/spec/features/valuation/budget_investments_spec.rb @@ -158,4 +158,238 @@ feature 'Valuation budget investments' do expect(page).to have_content("Old idea") end + feature 'Show' do + scenario 'visible for assigned valuators' do + administrator = create(:administrator, user: create(:user, username: 'Ana', email: 'ana@admins.org')) + valuator2 = create(:valuator, user: create(:user, username: 'Rick', email: 'rick@valuators.org')) + investment = create(:budget_investment, + budget: @budget, + price: 1234, + feasibility: 'unfeasible', + unfeasibility_explanation: 'It is impossible', + administrator: administrator) + investment.valuators << [@valuator, valuator2] + + visit valuation_budget_budget_investments_path(@budget) + + click_link investment.title + + expect(page).to have_content(investment.title) + expect(page).to have_content(investment.description) + expect(page).to have_content(investment.author.name) + expect(page).to have_content(investment.heading.name) + expect(page).to have_content('1234') + expect(page).to have_content('Unfeasible') + expect(page).to have_content('It is impossible') + expect(page).to have_content('Ana (ana@admins.org)') + + within('#assigned_valuators') do + expect(page).to have_content('Rachel (rachel@valuators.org)') + expect(page).to have_content('Rick (rick@valuators.org)') + end + end + + scenario 'visible for admins' do + logout + login_as create(:administrator).user + + administrator = create(:administrator, user: create(:user, username: 'Ana', email: 'ana@admins.org')) + valuator2 = create(:valuator, user: create(:user, username: 'Rick', email: 'rick@valuators.org')) + investment = create(:budget_investment, + budget: @budget, + price: 1234, + feasibility: 'unfeasible', + unfeasibility_explanation: 'It is impossible', + administrator: administrator) + investment.valuators << [@valuator, valuator2] + + visit valuation_budget_budget_investment_path(@budget, investment) + + expect(page).to have_content(investment.title) + expect(page).to have_content(investment.description) + expect(page).to have_content(investment.author.name) + expect(page).to have_content(investment.heading.name) + expect(page).to have_content('1234') + expect(page).to have_content('Unfeasible') + expect(page).to have_content('It is impossible') + expect(page).to have_content('Ana (ana@admins.org)') + + within('#assigned_valuators') do + expect(page).to have_content('Rachel (rachel@valuators.org)') + expect(page).to have_content('Rick (rick@valuators.org)') + end + end + + scenario 'not visible for not assigned valuators' do + logout + login_as create(:valuator).user + + valuator2 = create(:valuator, user: create(:user, username: 'Rick', email: 'rick@valuators.org')) + investment = create(:budget_investment, + budget: @budget, + price: 1234, + feasibility: 'unfeasible', + unfeasibility_explanation: 'It is impossible', + administrator: create(:administrator)) + investment.valuators << [@valuator, valuator2] + + expect { visit valuation_budget_budget_investment_path(@budget, investment) }.to raise_error "Not Found" + end + + end + + feature 'Valuate' do + background do + @investment = create(:budget_investment, + budget: @budget, + price: nil, + administrator: create(:administrator)) + @investment.valuators << @valuator + end + + scenario 'Dossier empty by default' do + visit valuation_budget_budget_investments_path(@budget) + click_link @investment.title + + within('#price') { expect(page).to have_content('Undefined') } + within('#price_first_year') { expect(page).to have_content('Undefined') } + within('#duration') { expect(page).to have_content('Undefined') } + within('#feasibility') { expect(page).to have_content('Undecided') } + expect(page).to_not have_content('Valuation finished') + expect(page).to_not have_content('Internal comments') + end + + scenario 'Edit dossier' do + visit valuation_budget_budget_investments_path(@budget) + within("#budget_investment_#{@investment.id}") do + click_link "Edit" + end + + fill_in 'budget_investment_price', with: '12345' + fill_in 'budget_investment_price_first_year', with: '9876' + fill_in 'budget_investment_price_explanation', with: 'Very cheap idea' + choose 'budget_investment_feasibility_feasible' + fill_in 'budget_investment_duration', with: '19 months' + fill_in 'budget_investment_internal_comments', with: 'Should be double checked by the urbanism area' + click_button 'Save changes' + + expect(page).to have_content "Dossier updated" + + visit valuation_budget_budget_investments_path(@budget) + click_link @investment.title + + within('#price') { expect(page).to have_content('12345') } + within('#price_first_year') { expect(page).to have_content('9876') } + expect(page).to have_content('Very cheap idea') + within('#duration') { expect(page).to have_content('19 months') } + within('#feasibility') { expect(page).to have_content('Feasible') } + expect(page).to_not have_content('Valuation finished') + expect(page).to have_content('Internal comments') + expect(page).to have_content('Should be double checked by the urbanism area') + end + + scenario 'Feasibility can be marked as pending' do + visit valuation_budget_budget_investment_path(@budget, @investment) + click_link 'Edit dossier' + + expect(find "#budget_investment_feasibility_undecided").to be_checked + choose 'budget_investment_feasibility_feasible' + click_button 'Save changes' + + visit edit_valuation_budget_budget_investment_path(@budget, @investment) + + expect(find "#budget_investment_feasibility_undecided").to_not be_checked + expect(find "#budget_investment_feasibility_feasible").to be_checked + + choose 'budget_investment_feasibility_undecided' + click_button 'Save changes' + + visit edit_valuation_budget_budget_investment_path(@budget, @investment) + expect(find "#budget_investment_feasibility_undecided").to be_checked + end + + scenario 'Feasibility selection makes proper fields visible', :js do + feasible_fields = ['Price (€)','Cost during the first year (€)','Price explanation','Time scope'] + unfeasible_fields = ['Feasibility explanation'] + any_feasibility_fields = ['Valuation finished','Internal comments'] + undecided_fields = feasible_fields + unfeasible_fields + any_feasibility_fields + + visit edit_valuation_budget_budget_investment_path(@budget, @investment) + + expect(find "#budget_investment_feasibility_undecided").to be_checked + + undecided_fields.each do |field| + expect(page).to have_content(field) + end + + choose 'budget_investment_feasibility_feasible' + + unfeasible_fields.each do |field| + expect(page).to_not have_content(field) + end + + (feasible_fields + any_feasibility_fields).each do |field| + expect(page).to have_content(field) + end + + choose 'budget_investment_feasibility_unfeasible' + + feasible_fields.each do |field| + expect(page).to_not have_content(field) + end + + (unfeasible_fields + any_feasibility_fields).each do |field| + expect(page).to have_content(field) + end + + click_button 'Save changes' + + visit edit_valuation_budget_budget_investment_path(@budget, @investment) + + expect(find "#budget_investment_feasibility_unfeasible").to be_checked + feasible_fields.each do |field| + expect(page).to_not have_content(field) + end + + (unfeasible_fields + any_feasibility_fields).each do |field| + expect(page).to have_content(field) + end + + choose 'budget_investment_feasibility_undecided' + + undecided_fields.each do |field| + expect(page).to have_content(field) + end + end + + scenario 'Finish valuation' do + visit valuation_budget_budget_investment_path(@budget, @investment) + click_link 'Edit dossier' + + check 'budget_investment_valuation_finished' + click_button 'Save changes' + + visit valuation_budget_budget_investments_path(@budget) + expect(page).to_not have_content @investment.title + click_link 'Valuation finished' + + expect(page).to have_content @investment.title + click_link @investment.title + expect(page).to have_content('Valuation finished') + end + + scenario 'Validates price formats' do + visit valuation_budget_budget_investments_path(@budget) + within("#budget_investment_#{@investment.id}") do + click_link "Edit" + end + + fill_in 'budget_investment_price', with: '12345,98' + fill_in 'budget_investment_price_first_year', with: '9876.6' + click_button 'Save changes' + + expect(page).to have_content('2 errors') + expect(page).to have_content('Only integer numbers', count: 2) + end + end end \ No newline at end of file