diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss
index e6a262d07..5bcba8bbd 100644
--- a/app/assets/stylesheets/participation.scss
+++ b/app/assets/stylesheets/participation.scss
@@ -275,6 +275,7 @@
.debate-new, .debate-edit,
.proposal-new, .proposal-edit,
+.budget-proposal-new, .budget-proposal-edit,
.spending-proposal-new, .spending-proposal-edit {
.icon-debates {
diff --git a/app/controllers/management/budget_investments_controller.rb b/app/controllers/management/budget_investments_controller.rb
new file mode 100644
index 000000000..31706ce89
--- /dev/null
+++ b/app/controllers/management/budget_investments_controller.rb
@@ -0,0 +1,78 @@
+class Management::BudgetInvestmentsController < Management::BaseController
+
+ before_action :only_verified_users, except: :print
+ before_action :set_budget_investment, only: [:vote, :show]
+
+ def index
+ @budget_investments = apply_filters_and_search(Budget::Investment).order(cached_votes_up: :desc).page(params[:page]).for_render
+ set_budget_investment_votes(@budget_investments)
+ end
+
+ def new
+ @investment = Budget::Investment.new
+ end
+
+ def create
+ @budget_investment = Budget::Investment.new(budget_investment_params)
+ @budget_investment.author = managed_user
+
+ if @budget_investment.save
+ redirect_to management_budget_investment_path(@budget_investment), notice: t('flash.actions.create.notice', resource_name: t("activerecord.models.budget_investment", count: 1))
+ else
+ render :new
+ end
+ end
+
+ def show
+ set_budget_investment_votes(@budget_investment)
+ end
+
+ def vote
+ @budget_investment.register_vote(managed_user, 'yes')
+ set_budget_investment_votes(@budget_investment)
+ end
+
+ def print
+ params[:geozone] ||= 'all'
+ @budget_investments = apply_filters_and_search(Budget::Investment).order(cached_votes_up: :desc).for_render.limit(15)
+ set_budget_investment_votes(@budget_investments)
+ end
+
+ private
+
+ def set_budget_investment
+ @budget_investment = Budget::Investment.find(params[:id])
+ end
+
+ def budget_investment_params
+ params.require(:budget_investment).permit(:title, :description, :external_url, :geozone_id, :terms_of_service)
+ end
+
+ def only_verified_users
+ check_verified_user t("management.budget_investments.alert.unverified_user")
+ end
+
+ # This should not be necessary. Maybe we could create a specific show view for managers.
+ def set_budget_investment_votes(budget_investments)
+ @budget_investment_votes = managed_user ? managed_user.budget_investment_votes(budget_investments) : {}
+ end
+
+ def set_geozone_name
+ if params[:geozone] == 'all'
+ @geozone_name = t('geozones.none')
+ else
+ @geozone_name = Geozone.find(params[:geozone]).name
+ end
+ end
+
+ def apply_filters_and_search(target)
+ target = params[:unfeasible].present? ? target.unfeasible : target.not_unfeasible
+ if params[:geozone].present?
+ target = target.by_geozone(params[:geozone])
+ set_geozone_name
+ end
+ target = target.search(params[:search]) if params[:search].present?
+ target
+ end
+
+end
diff --git a/app/views/admin/shared/_budget_investment_search.html.erb b/app/views/admin/shared/_budget_investment_search.html.erb
new file mode 100644
index 000000000..92bd8e24a
--- /dev/null
+++ b/app/views/admin/shared/_budget_investment_search.html.erb
@@ -0,0 +1,10 @@
+<%= form_for(Budget::Investment.new, url: url, as: :budget_investment, method: :get) do |f| %>
+
+
+ <%= text_field_tag :search, "", placeholder: t("admin.shared.budget_investment_search.placeholder") %>
+
+
+ <%= f.submit t("admin.shared.budget_investment_search.button"), class: "button" %>
+
+
+<% end %>
diff --git a/app/views/management/_menu.html.erb b/app/views/management/_menu.html.erb
index aa154212e..1a24bba29 100644
--- a/app/views/management/_menu.html.erb
+++ b/app/views/management/_menu.html.erb
@@ -44,6 +44,20 @@
<% end %>
+ >
+ <%= link_to new_management_budget_investment_path do %>
+
+ <%= t("management.menu.create_budget_investment") %>
+ <% end %>
+
+
+ >
+ <%= link_to management_budget_investments_path do %>
+
+ <%= t("management.menu.support_budget_investments") %>
+ <% end %>
+
+
>
<%= link_to print_management_proposals_path do %>
@@ -58,6 +72,14 @@
<% end %>
+ >
+ <%= link_to print_management_budget_investments_path do %>
+
+ <%= t("management.menu.print_budget_investments") %>
+ <% end %>
+
+
+
<%= link_to new_management_user_invite_path do %>
diff --git a/app/views/management/budget_investments/_budget_investment.html.erb b/app/views/management/budget_investments/_budget_investment.html.erb
new file mode 100644
index 000000000..345e8b377
--- /dev/null
+++ b/app/views/management/budget_investments/_budget_investment.html.erb
@@ -0,0 +1 @@
+<%= render partial: 'budgets/investments/investment', locals: {investment: budget_investment} %>
diff --git a/app/views/management/budget_investments/_votes.html.erb b/app/views/management/budget_investments/_votes.html.erb
new file mode 100644
index 000000000..579c5d6c3
--- /dev/null
+++ b/app/views/management/budget_investments/_votes.html.erb
@@ -0,0 +1,2 @@
+<%= render 'budgets/investments/votes',
+ { investment: budget_investment, vote_url: vote_management_budget_investment_path(budget_investment.budget, budget_investment, value: 'yes') } %>
diff --git a/app/views/management/budget_investments/index.html.erb b/app/views/management/budget_investments/index.html.erb
new file mode 100644
index 000000000..1f52524ef
--- /dev/null
+++ b/app/views/management/budget_investments/index.html.erb
@@ -0,0 +1,25 @@
+
+
+ <%= render 'admin/shared/budget_investment_search', url: management_budget_investments_path %>
+
+
+
+
+
+
+
+ <%= content_tag(:h2, t("management.budget_investments.filters.unfeasible")) if params[:unfeasible].present? %>
+ <%= content_tag(:h2, t("management.budget_investments.filters.by_geozone", geozone: @geozone_name)) if @geozone_name.present? %>
+ <% if params[:search].present? %>
+
+ <%= page_entries_info @budget_investments %>
+ <%= t("management.budget_investments.search_results", count: @budget_investments.size, search_term: params[:search]) %>
+
+ <% end %>
+
+
+ <%= render @budget_investments %>
+ <%= paginate @budget_investments %>
+
+
+
diff --git a/app/views/management/budget_investments/new.html.erb b/app/views/management/budget_investments/new.html.erb
new file mode 100644
index 000000000..59e4d30e9
--- /dev/null
+++ b/app/views/management/budget_investments/new.html.erb
@@ -0,0 +1,12 @@
+
+
+
+ <%= render '/shared/print' %>
+
+
+
+
<%= t("management.budget_investments.create") %>
+ <%= render "budgets/investments/form", form_url: management_budget_investments_url %>
+
+
+
diff --git a/app/views/management/budget_investments/print.html.erb b/app/views/management/budget_investments/print.html.erb
new file mode 100644
index 000000000..785202e1c
--- /dev/null
+++ b/app/views/management/budget_investments/print.html.erb
@@ -0,0 +1,34 @@
+
+
+
+
+
+ <%= form_tag print_management_budget_investments_path, method: :get, enforce_utf8: false do %>
+
+ <%= select_tag :geozone,
+ options_for_select(geozone_select_options.unshift([t("geozones.none"), "all"]), params[:geozone]),
+ { label: false,
+ class: "js-submit-on-change" } %>
+
+ <% end %>
+
+
+ <%= t('management.budget_investments.print.print_button') %>
+
+
+
+
+ <%= content_tag(:h2, t("management.budget_investments.filters.unfeasible"), class: "inline-block") if params[:unfeasible].present? %>
+ <%= content_tag(:h2, t("management.budget_investments.filters.by_geozone", geozone: @geozone_name), class: "inline-block") if @geozone_name.present? %>
+ <%= content_tag(:h2, t("management.budget_investments.search_results", count: @budget_investments.size, search_term: params[:search]), class: "inline-block") if params[:search].present? %>
+
+
+ <%= render @budget_investments %>
+
+
+
<%= t("management.print.budget_investments_info") %>
+ <%= t("management.print.budget_investments_note") %>
+
+
+
+
diff --git a/app/views/management/budget_investments/show.html.erb b/app/views/management/budget_investments/show.html.erb
new file mode 100644
index 000000000..9b558b845
--- /dev/null
+++ b/app/views/management/budget_investments/show.html.erb
@@ -0,0 +1,3 @@
+<%= render '/shared/print' %>
+
+<%= render template: 'budgets/investments/show' %>
diff --git a/app/views/management/budget_investments/vote.js.erb b/app/views/management/budget_investments/vote.js.erb
new file mode 100644
index 000000000..74705cf2b
--- /dev/null
+++ b/app/views/management/budget_investments/vote.js.erb
@@ -0,0 +1 @@
+<%= render template: 'budgets/investments/vote' %>
diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml
index c9bd21c18..8511dbfc4 100755
--- a/config/locales/admin.en.yml
+++ b/config/locales/admin.en.yml
@@ -283,6 +283,9 @@ en:
proposal_search:
button: Search
placeholder: Search proposals by title, code, description or question
+ budget_investment_search:
+ button: Search
+ placeholder: Search investments by title or description
spending_proposal_search:
button: Search
placeholder: Search spending proposals by title or description
diff --git a/config/locales/management.en.yml b/config/locales/management.en.yml
index fa02e5bc3..8daa53235 100644
--- a/config/locales/management.en.yml
+++ b/config/locales/management.en.yml
@@ -50,6 +50,9 @@ en:
create_spending_proposal: Create spending proposal
print_spending_proposals: Print spending proposals
support_spending_proposals: Support spending proposals
+ create_budget_investment: Create budget investment
+ print_budget_investment: Print budget investment
+ support_budget_investment: Support budget investment
users: Users
edit_user_accounts: Edit user account
user_invites: User's invites
@@ -62,8 +65,8 @@ en:
proposals_info: Create yor proposal on http://url.consul
proposals_note: The proposals more supported will be voted. If are accepted by a majority, the city Council shall be carried out.
proposals_title: 'Proposals:'
- spending_proposals_info: Participate at http://url.consul
- spending_proposals_note: Participatory budget will be assigned to the most votes spending proposals.
+ budget_investments_info: Participate at http://url.consul
+ budget_investments_note: Participatory budget will be assigned to the most voted budget investment.
print_info: Print this info
proposals:
alert:
@@ -71,6 +74,18 @@ en:
create_proposal: Create proposal
print:
print_button: Print
+ budget_investments:
+ alert:
+ unverified_user: User is not verified
+ create: Create budget investment
+ filters:
+ unfeasible: Unfeasible investment
+ by_geozone: "Investment with scope: %{geozone}"
+ print:
+ print_button: Print
+ search_results:
+ one: " containing the term '%{search_term}'"
+ other: " containing the term '%{search_term}'"
spending_proposals:
alert:
unverified_user: User is not verified
@@ -106,4 +121,4 @@ en:
title: User's invites
create:
success_html: %{count} invitations have been sent.
- title: User's invites
\ No newline at end of file
+ title: User's invites
diff --git a/config/routes.rb b/config/routes.rb
index c10b4ba2b..245701634 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -282,6 +282,11 @@ Rails.application.routes.draw do
post :vote, on: :member
get :print, on: :collection
end
+
+ resources :budget_investments, only: [:index, :new, :create, :show] do
+ post :vote, on: :member
+ get :print, on: :collection
+ end
end
if Rails.env.development?
diff --git a/spec/features/management/budget_investments_spec.rb b/spec/features/management/budget_investments_spec.rb
new file mode 100644
index 000000000..7d793bfd3
--- /dev/null
+++ b/spec/features/management/budget_investments_spec.rb
@@ -0,0 +1,224 @@
+require 'rails_helper'
+
+feature 'Budget Investments' do
+
+ background do
+ login_as_manager
+ end
+
+ context "Create" do
+
+ scenario 'Creating budget investments on behalf of someone' do
+ user = create(:user, :level_two)
+ login_managed_user(user)
+
+ save_and_open_page
+ click_link "Create budget investment"
+
+ within(".account-info") do
+ expect(page).to have_content "Identified as"
+ expect(page).to have_content "#{user.username}"
+ expect(page).to have_content "#{user.email}"
+ expect(page).to have_content "#{user.document_number}"
+ end
+
+ fill_in 'budget_investment_title', with: 'Build a park in my neighborhood'
+ fill_in 'budget_investment_description', with: 'There is no parks here...'
+ fill_in 'budget_investment_external_url', with: 'http://moarparks.com'
+ check 'budget_investment_terms_of_service'
+
+ click_button 'Create'
+
+ expect(page).to have_content 'budget investment created successfully.'
+
+ expect(page).to have_content 'Build a park in my neighborhood'
+ expect(page).to have_content 'There is no parks here...'
+ expect(page).to have_content 'All city'
+ expect(page).to have_content 'http://moarparks.com'
+ expect(page).to have_content user.name
+ expect(page).to have_content I18n.l(Budget::Investment.last.created_at.to_date)
+
+ expect(current_path).to eq(management_budget_investment_path(Budget::Investment.last))
+ end
+
+ scenario "Should not allow unverified users to create budget investments" do
+ user = create(:user)
+ login_managed_user(user)
+
+ click_link "Create budget investment"
+
+ expect(page).to have_content "User is not verified"
+ end
+ end
+
+ context "Searching" do
+ scenario "by title" do
+ budget_investment1 = create(:budget_investment, title: "Show me what you got")
+ budget_investment2 = create(:budget_investment, title: "Get Schwifty")
+
+ user = create(:user, :level_two)
+ login_managed_user(user)
+
+ click_link "Support budget investments"
+
+ fill_in "search", with: "what you got"
+ click_button "Search"
+
+ expect(current_path).to eq(management_budget_investments_path)
+
+ within("#investment-projects") do
+ expect(page).to have_css('.investment-project', count: 1)
+ expect(page).to have_content(budget_investment1.title)
+ expect(page).to_not have_content(budget_investment2.title)
+ expect(page).to have_css("a[href='#{management_budget_investment_path(budget_investment1)}']", text: budget_investment1.title)
+ expect(page).to have_css("a[href='#{management_budget_investment_path(budget_investment1)}']", text: budget_investment1.description)
+ end
+ end
+
+ scenario "by district" do
+ budget_investment1 = create(:budget_investment, title: "Hey ho", geozone_id: create(:geozone, name: "District 9").id)
+ budget_investment2 = create(:budget_investment, title: "Let's go", geozone_id: create(:geozone, name: "Area 52").id)
+
+ user = create(:user, :level_two)
+ login_managed_user(user)
+
+ click_link "Support budget investments"
+
+ fill_in "search", with: "Area 52"
+ click_button "Search"
+
+ expect(current_path).to eq(management_budget_investments_path)
+
+ within("#investment-projects") do
+ expect(page).to have_css('.investment-project', count: 1)
+ expect(page).to_not have_content(budget_investment1.title)
+ expect(page).to have_content(budget_investment2.title)
+ expect(page).to have_css("a[href='#{management_budget_investment_path(budget_investment2)}']", text: budget_investment2.title)
+ expect(page).to have_css("a[href='#{management_budget_investment_path(budget_investment2)}']", text: budget_investment2.description)
+ end
+ end
+ end
+
+ scenario "Listing" do
+ budget_investment1 = create(:budget_investment, title: "Show me what you got")
+ budget_investment2 = create(:budget_investment, title: "Get Schwifty")
+
+ user = create(:user, :level_two)
+ login_managed_user(user)
+
+ click_link "Support budget investments"
+
+ expect(current_path).to eq(management_budget_investments_path)
+
+ within(".account-info") do
+ expect(page).to have_content "Identified as"
+ expect(page).to have_content "#{user.username}"
+ expect(page).to have_content "#{user.email}"
+ expect(page).to have_content "#{user.document_number}"
+ end
+
+ within("#investment-projects") do
+ expect(page).to have_css('.investment-project', count: 2)
+ expect(page).to have_css("a[href='#{management_budget_investment_path(budget_investment1)}']", text: budget_investment1.title)
+ expect(page).to have_css("a[href='#{management_budget_investment_path(budget_investment1)}']", text: budget_investment1.description)
+ expect(page).to have_css("a[href='#{management_budget_investment_path(budget_investment2)}']", text: budget_investment2.title)
+ expect(page).to have_css("a[href='#{management_budget_investment_path(budget_investment2)}']", text: budget_investment2.description)
+ end
+ end
+
+ context "Voting" do
+
+ scenario 'Voting budget investments on behalf of someone in index view', :js do
+ budget_investment = create(:budget_investment)
+
+ user = create(:user, :level_two)
+ login_managed_user(user)
+
+ click_link "Support budget investments"
+
+ within("#investment-projects") do
+ find('.in-favor a').click
+
+ expect(page).to have_content "1 support"
+ expect(page).to have_content "You have already supported this. Share it!"
+ end
+ expect(current_path).to eq(management_budget_investments_path)
+ end
+
+ scenario 'Voting budget investments on behalf of someone in show view', :js do
+ budget_investment = create(:budget_investment)
+
+ user = create(:user, :level_two)
+ login_managed_user(user)
+
+ click_link "Support budget investments"
+
+ within("#investment-projects") do
+ click_link budget_investment.title
+ end
+
+ find('.in-favor a').click
+ expect(page).to have_content "1 support"
+ expect(page).to have_content "You have already supported this. Share it!"
+ expect(current_path).to eq(management_budget_investment_path(budget_investment))
+ end
+
+ scenario "Should not allow unverified users to vote proposals" do
+ budget_investment = create(:budget_investment)
+
+ user = create(:user)
+ login_managed_user(user)
+
+ click_link "Support budget investments"
+
+ expect(page).to have_content "User is not verified"
+ end
+ end
+
+ context "Printing" do
+
+ scenario 'Printing budget investments' do
+ 16.times { create(:budget_investment, geozone_id: nil) }
+
+ click_link "Print budget investments"
+
+ expect(page).to have_css('.investment-project', count: 15)
+ expect(page).to have_css("a[href='javascript:window.print();']", text: 'Print')
+ end
+
+ scenario "Filtering budget investments by geozone to be printed", :js do
+ district_9 = create(:geozone, name: "District Nine")
+ create(:budget_investment, title: 'Change district 9', geozone: district_9, cached_votes_up: 10)
+ create(:budget_investment, title: 'Destroy district 9', geozone: district_9, cached_votes_up: 100)
+ create(:budget_investment, title: 'Nuke district 9', geozone: district_9, cached_votes_up: 1)
+ create(:budget_investment, title: 'Add new districts to the city', geozone_id: nil)
+
+ user = create(:user, :level_two)
+ login_managed_user(user)
+
+ click_link "Print budget investments"
+
+ expect(page).to have_content "Budget investments with scope: All city"
+
+ within '#investment-projects' do
+ expect(page).to have_content('Add new districts to the city')
+ expect(page).to_not have_content('Change district 9')
+ expect(page).to_not have_content('Destroy district 9')
+ expect(page).to_not have_content('Nuke district 9')
+ end
+
+ select 'District Nine', from: 'geozone'
+
+ expect(page).to have_content "Investment projects with scope: District Nine"
+ expect(current_url).to include("geozone=#{district_9.id}")
+
+ within '#investment-projects' do
+ expect(page).to_not have_content('Add new districts to the city')
+ expect('Destroy district 9').to appear_before('Change district 9')
+ expect('Change district 9').to appear_before('Nuke district 9')
+ end
+ end
+
+ end
+
+end