diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 98e378e69..aa27109d9 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -40,6 +40,7 @@ //= require advanced_search //= require registration_form //= require suggest +//= require forms var initialize_modules = function() { App.Comments.initialize(); @@ -55,6 +56,7 @@ var initialize_modules = function() { App.AdvancedSearch.initialize(); App.RegistrationForm.initialize(); App.Suggest.initialize(); + App.Forms.initialize(); }; $(function(){ diff --git a/app/assets/javascripts/forms.js.coffee b/app/assets/javascripts/forms.js.coffee new file mode 100644 index 000000000..a7d26cc02 --- /dev/null +++ b/app/assets/javascripts/forms.js.coffee @@ -0,0 +1,12 @@ +App.Forms = + + initialize: -> + $('.js-submit-on-change').unbind('change').on('change', -> + $(this).closest('form').submit() + false + ) + + $('.js-toggle-link').unbind('click').on('click', -> + $($(this).data('toggle-selector')).toggle("down") + false + ) diff --git a/app/assets/javascripts/stat_graphs.js b/app/assets/javascripts/stat_graphs.js index e21d88ff0..c5bc5b538 100644 --- a/app/assets/javascripts/stat_graphs.js +++ b/app/assets/javascripts/stat_graphs.js @@ -8,7 +8,6 @@ var initialize_stats_modules = function() { }; $(function(){ - $(document).ready(initialize_stats_modules); $(document).on('page:load', initialize_stats_modules); $(document).on('ajax:complete', initialize_stats_modules); diff --git a/app/controllers/admin/spending_proposals_controller.rb b/app/controllers/admin/spending_proposals_controller.rb index 559a7afda..b758bd10d 100644 --- a/app/controllers/admin/spending_proposals_controller.rb +++ b/app/controllers/admin/spending_proposals_controller.rb @@ -1,27 +1,27 @@ class Admin::SpendingProposalsController < Admin::BaseController include FeatureFlags - - has_filters %w{unresolved accepted rejected}, only: :index + feature_flag :spending_proposals load_and_authorize_resource - feature_flag :spending_proposals - def index - @spending_proposals = @spending_proposals.includes([:geozone]).send(@current_filter).order(created_at: :desc).page(params[:page]) + @spending_proposals = @spending_proposals.includes([:geozone], [administrator: :user]).order(created_at: :desc).page(params[:page]) end def show + @admins = Administrator.includes(:user).all + @valuators = Valuator.includes(:user).all.order("users.username ASC") end - def accept - @spending_proposal.accept - redirect_to request.query_parameters.merge(action: :index) + def assign_admin + @spending_proposal.update(params.require(:spending_proposal).permit(:administrator_id)) + render nothing: true end - def reject - @spending_proposal.reject - redirect_to request.query_parameters.merge(action: :index) + def assign_valuators + params[:spending_proposal] ||= {} + params[:spending_proposal][:valuator_ids] ||= [] + @spending_proposal.update(params.require(:spending_proposal).permit(valuator_ids: [])) end end diff --git a/app/models/spending_proposal.rb b/app/models/spending_proposal.rb index c023f83f4..6339fd545 100644 --- a/app/models/spending_proposal.rb +++ b/app/models/spending_proposal.rb @@ -4,10 +4,11 @@ class SpendingProposal < ActiveRecord::Base apply_simple_captcha - RESOLUTIONS = ["accepted", "rejected"] - belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id' belongs_to :geozone + belongs_to :administrator + has_many :valuation_assignments, dependent: :destroy + has_many :valuators, through: :valuation_assignments validates :title, presence: true validates :author, presence: true @@ -15,42 +16,10 @@ class SpendingProposal < ActiveRecord::Base validates :title, length: { in: 4..SpendingProposal.title_max_length } validates :description, length: { maximum: SpendingProposal.description_max_length } - validates :resolution, inclusion: { in: RESOLUTIONS, allow_nil: true } validates :terms_of_service, acceptance: { allow_nil: false }, on: :create - scope :accepted, -> { where(resolution: "accepted") } - scope :rejected, -> { where(resolution: "rejected") } - scope :unresolved, -> { where(resolution: nil) } - - def accept - update_attribute(:resolution, "accepted") - end - - def reject - update_attribute(:resolution, "rejected") - end - - def accepted? - resolution == "accepted" - end - - def rejected? - resolution == "rejected" - end - - def unresolved? - resolution.blank? - end - - def legality - case legal - when true - "legal" - when false - "not_legal" - else - "undefined" - end + def description + super.try :html_safe end def feasibility @@ -64,8 +33,4 @@ class SpendingProposal < ActiveRecord::Base end end - def description - super.try :html_safe - end - end diff --git a/app/models/valuation_assignment.rb b/app/models/valuation_assignment.rb new file mode 100644 index 000000000..c26b7087f --- /dev/null +++ b/app/models/valuation_assignment.rb @@ -0,0 +1,4 @@ +class ValuationAssignment < ActiveRecord::Base + belongs_to :valuator + belongs_to :spending_proposal +end diff --git a/app/models/valuator.rb b/app/models/valuator.rb index c513e997e..9e18d2cca 100644 --- a/app/models/valuator.rb +++ b/app/models/valuator.rb @@ -2,5 +2,8 @@ class Valuator < ActiveRecord::Base belongs_to :user, touch: true delegate :name, :email, to: :user + has_many :valuation_assignments, dependent: :destroy + has_many :spending_proposals, through: :valuation_assignments + validates :user_id, presence: true, uniqueness: true end diff --git a/app/views/admin/spending_proposals/_assigned_valuators.html.erb b/app/views/admin/spending_proposals/_assigned_valuators.html.erb new file mode 100644 index 000000000..09e144dcd --- /dev/null +++ b/app/views/admin/spending_proposals/_assigned_valuators.html.erb @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/app/views/admin/spending_proposals/assign_valuators.js.coffee b/app/views/admin/spending_proposals/assign_valuators.js.coffee new file mode 100644 index 000000000..afe581e59 --- /dev/null +++ b/app/views/admin/spending_proposals/assign_valuators.js.coffee @@ -0,0 +1 @@ +$('#assigned_valuators').html("<%= j(render 'assigned_valuators') %>") \ No newline at end of file diff --git a/app/views/admin/spending_proposals/index.html.erb b/app/views/admin/spending_proposals/index.html.erb index 2b8cb639e..a0ed583cc 100644 --- a/app/views/admin/spending_proposals/index.html.erb +++ b/app/views/admin/spending_proposals/index.html.erb @@ -1,7 +1,5 @@

<%= t("admin.spending_proposals.index.title") %>

-<%= render 'shared/filter_subnav', i18n_namespace: "admin.spending_proposals.index" %> -

<%= page_entries_info @spending_proposals %>

@@ -14,22 +12,6 @@ - <% end %>
<%= geozone_name(spending_proposal) %> - <% unless spending_proposal.accepted? %> - <%= link_to t("admin.spending_proposals.actions.accept"), - accept_admin_spending_proposal_path(spending_proposal, request.query_parameters), - method: :put, - data: { confirm: t("admin.actions.confirm") }, - class: "button radius tiny success no-margin" %> - <% end %> - <% unless spending_proposal.rejected? %> - <%= link_to t("admin.spending_proposals.actions.reject"), - reject_admin_spending_proposal_path(spending_proposal, request.query_parameters), - method: :put, - data: { confirm: t("admin.actions.confirm") }, - class: "button radius tiny warning right" %> - <% end %> -
diff --git a/app/views/admin/spending_proposals/show.html.erb b/app/views/admin/spending_proposals/show.html.erb index 4d3b57935..008921ffe 100644 --- a/app/views/admin/spending_proposals/show.html.erb +++ b/app/views/admin/spending_proposals/show.html.erb @@ -1,44 +1,78 @@ -

<%= @spending_proposal.title %>

+

<%= t("admin.spending_proposals.show.heading") %> <%= @spending_proposal.id %>

+

<%= @spending_proposal.title %>

-<%= safe_html_with_links @spending_proposal.description.html_safe %> +<%= safe_html_with_links @spending_proposal.description %> <% if @spending_proposal.external_url.present? %>

<%= text_with_links @spending_proposal.external_url %>

<% end %> -

<%= t("admin.spending_proposals.show.by") %>: +

<%= t("admin.spending_proposals.show.info") %>

+ +

<%= t("admin.spending_proposals.show.by") %>: <%= link_to @spending_proposal.author.name, admin_user_path(@spending_proposal.author) %>

-

<%= t("admin.spending_proposals.show.association_name") %>: - <%= @spending_proposal.association_name %> -

-

<%= t("admin.spending_proposals.show.geozone") %>: + +<% if @spending_proposal.association_name.present? %> +

<%= t("admin.spending_proposals.show.association_name") %>: + <%= @spending_proposal.association_name %> +

+<% end %> + +

<%= t("admin.spending_proposals.show.geozone") %>: <%= geozone_name(@spending_proposal) %>

-

<%= l @spending_proposal.created_at, format: :datetime %>

-

- <% unless @spending_proposal.accepted? %> - <%= link_to t("admin.spending_proposals.actions.accept"), - accept_admin_spending_proposal_path(@spending_proposal), - method: :put, - data: { confirm: t("admin.actions.confirm") }, - class: "button radius tiny success no-margin" %> - <% end %> - <% unless @spending_proposal.rejected? %> - <%= link_to t("admin.spending_proposals.actions.reject"), - reject_admin_spending_proposal_path(@spending_proposal), - method: :put, - data: { confirm: t("admin.actions.confirm") }, - class: "button radius tiny warning" %> - <% end %> +

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

+

<%= t("admin.spending_proposals.show.responsibles") %>

-

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

+

<%= t("admin.spending_proposals.show.assigned_admin") %>: + <%= form_for(@spending_proposal, url: assign_admin_admin_spending_proposal_path(@spending_proposal), remote: true, html: {id: 'administrator_assignment_form'}) do |f| %> + <%= f.select :administrator_id, @admins.collect { |a| [ "#{a.name} (#{a.email})", a.id ] }, {include_blank: t("admin.spending_proposals.show.undefined"), label: false}, class: "js-submit-on-change" %> + <% end %> +

-

<%= t("admin.spending_proposals.show.price") %>: <%= @spending_proposal.price %>

-

<%= t("admin.spending_proposals.show.legality") %>: <%= t("admin.spending_proposals.show.#{@spending_proposal.legality}") %>

-

<%= t("admin.spending_proposals.show.feasibility") %>: <%= t("admin.spending_proposals.show.#{@spending_proposal.feasibility}") %>

+

<%= t("admin.spending_proposals.show.assigned_valuators") %>:

+
+ <%= render "assigned_valuators" %> +
-<%= safe_html_with_links(@spending_proposal.explanation.html_safe) if @spending_proposal.explanation %> +

<%= link_to t("admin.spending_proposals.show.assign_valuators"), "", class: "js-toggle-link", data: {"toggle-selector" => "#valuators-assign-list"} %>

+ + + + +

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

+ +

<%= t("admin.spending_proposals.show.price") %> (<%= t("admin.spending_proposals.show.currency") %>): + <%= @spending_proposal.price.present? ? @spending_proposal.price : t("admin.spending_proposals.show.undefined") %> +

+<%= simple_format(safe_html_with_links(@spending_proposal.price_explanation.html_safe), {}, sanitize: false) if @spending_proposal.price_explanation.present? %> + +

<%= t("admin.spending_proposals.show.feasibility") %>: + <%= t("admin.spending_proposals.show.#{@spending_proposal.feasibility}") %> +

+<%= simple_format(safe_html_with_links(@spending_proposal.feasible_explanation.html_safe), {}, sanitize: false) if @spending_proposal.feasible_explanation.present? %> + +<% if @spending_proposal.valuation_finished %> +

<%= t("admin.spending_proposals.show.valuation_finished") %> +<% end %> + +<% if @spending_proposal.internal_comments.present? %> +

<%= t("admin.spending_proposals.show.internal_comments") %>

+ <%= simple_format(safe_html_with_links(@spending_proposal.internal_comments.html_safe), {}, sanitize: false) %> +<% end %> diff --git a/app/views/spending_proposals/show.html.erb b/app/views/spending_proposals/show.html.erb index 95432fba9..987118a6a 100644 --- a/app/views/spending_proposals/show.html.erb +++ b/app/views/spending_proposals/show.html.erb @@ -23,11 +23,6 @@ <% end %> - <% if @spending_proposal.resolution.present? %> -
- <%= @spending_proposal.resolution %> -
- <% end %> diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index 1711c9221..7d3aef540 100755 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -137,9 +137,6 @@ en: button: Search placeholder: Search user by name or email' spending_proposals: - actions: - accept: Accept - reject: Reject index: filter: Filter filters: @@ -148,18 +145,26 @@ en: unresolved: Unresolved title: Spending proposals for participatory budgeting show: + heading: Investment project + info: Author info association_name: Asociación by: Sent by + sent: Sent at geozone: Scope dossier: Dossier price: Price - legality: Legality - legal: Legal - not_legal: Not legal + currency: "€" feasibility: Feasibility feasible: Feasible not_feasible: Not feasible undefined: Undefined + valuation_finished: Valuation finished + internal_comments: Internal comments + responsibles: Responsibles + assigned_admin: Assigned admin + assigned_valuators: Assigned valuators + assign_valuators: Assign valuators + assign: Assign stats: show: stats_title: Stats diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 4ccdba39e..8d2815423 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -57,7 +57,7 @@ es: officials: Cargos públicos organizations: Organizaciones settings: Configuración global - spending_proposals: Propuestas de gasto + spending_proposals: Propuestas de inversión stats: Estadísticas moderators: index: @@ -137,9 +137,6 @@ es: button: Buscar placeholder: Buscar usuario por nombre o email spending_proposals: - actions: - accept: Aceptar - reject: Rechazar index: filter: Filtro filters: @@ -148,18 +145,26 @@ es: unresolved: Sin resolver title: Propuestas de gasto para presupuestos participativos show: + heading: Propuesta de inversión + info: Datos de envío association_name: Asociación by: Enviada por + sent: Fecha de creación geozone: Ámbito dossier: Informe price: Coste - legality: Legalidad - legal: Legal - not_legal: No legal + currency: "€" feasibility: Viabilidad feasible: Viable not_feasible: No viable undefined: Sin definir + valuation_finished: Informe finalizado + internal_comments: Commentarios internos + responsibles: Responsables + assigned_admin: Administrador asignado + assigned_valuators: Evaluadores asignados + assign_valuators: Asignar evaluadores + assign: Asignar stats: show: stats_title: Estadísticas diff --git a/config/routes.rb b/config/routes.rb index 7a8a99881..302ce602c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -131,10 +131,10 @@ Rails.application.routes.draw do end end - resources :spending_proposals, only: [:index, :show], path: 'investment_projects' do + resources :spending_proposals, only: [:index, :show] do member do - put :accept - put :reject + patch :assign_admin + patch :assign_valuators end end diff --git a/db/dev_seeds.rb b/db/dev_seeds.rb index 0bee91cb2..f36ed5b36 100644 --- a/db/dev_seeds.rb +++ b/db/dev_seeds.rb @@ -41,6 +41,9 @@ admin.create_administrator moderator = create_user('mod@madrid.es', 'mod') moderator.create_moderator +valuator = create_user('valuator@madrid.es', 'valuator') +valuator.create_valuator + (1..10).each do |i| org_name = Faker::Company.name org_user = create_user("org#{i}@madrid.es", org_name) @@ -247,8 +250,6 @@ end puts "Creating Spending Proposals" -resolutions = ["accepted", "rejected", nil] - (1..30).each do |i| geozone = Geozone.reorder("RANDOM()").first author = User.reorder("RANDOM()").first @@ -258,12 +259,17 @@ resolutions = ["accepted", "rejected", nil] external_url: Faker::Internet.url, description: description, created_at: rand((Time.now - 1.week) .. Time.now), - resolution: resolutions.sample, geozone: [geozone, nil].sample, terms_of_service: "1") puts " #{spending_proposal.title}" end +puts "Creating Valuation Assignments" + +(1..17).to_a.sample.times do + SpendingProposal.reorder("RANDOM()").first.valuators << valuator.valuator +end + puts "Creating Legislation" Legislation.create!(title: 'Participatory Democracy', body: 'In order to achieve...') diff --git a/db/migrate/20160224101038_change_spending_proposals_fields.rb b/db/migrate/20160224101038_change_spending_proposals_fields.rb new file mode 100644 index 000000000..22a7270cb --- /dev/null +++ b/db/migrate/20160224101038_change_spending_proposals_fields.rb @@ -0,0 +1,16 @@ +class ChangeSpendingProposalsFields < ActiveRecord::Migration + def change + remove_index :spending_proposals, column: :resolution + + remove_column :spending_proposals, :legal, :boolean + remove_column :spending_proposals, :resolution, :string + remove_column :spending_proposals, :explanation, :text + + add_column :spending_proposals, :price_explanation, :text + add_column :spending_proposals, :feasible_explanation, :text + add_column :spending_proposals, :internal_comments, :text + add_column :spending_proposals, :valuation_finished, :boolean, default: false + add_column :spending_proposals, :explanations_log, :text + add_column :spending_proposals, :administrator_id, :integer + end +end diff --git a/db/migrate/20160224123110_create_valuation_assignments.rb b/db/migrate/20160224123110_create_valuation_assignments.rb new file mode 100644 index 000000000..7ec9a9df4 --- /dev/null +++ b/db/migrate/20160224123110_create_valuation_assignments.rb @@ -0,0 +1,9 @@ +class CreateValuationAssignments < ActiveRecord::Migration + def change + create_table :valuation_assignments do |t| + t.belongs_to :valuator + t.belongs_to :spending_proposal + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 2c948febc..fd1e0e484 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160222145100) do +ActiveRecord::Schema.define(version: 20160224123110) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -108,10 +108,10 @@ ActiveRecord::Schema.define(version: 20160222145100) do t.string "visit_id" t.datetime "hidden_at" t.integer "flags_count", default: 0 - t.datetime "ignored_flag_at" t.integer "cached_votes_total", default: 0 t.integer "cached_votes_up", default: 0 t.integer "cached_votes_down", default: 0 + t.datetime "ignored_flag_at" t.integer "comments_count", default: 0 t.datetime "confirmed_hide_at" t.integer "cached_anonymous_votes_total", default: 0 @@ -129,6 +129,7 @@ ActiveRecord::Schema.define(version: 20160222145100) do add_index "debates", ["cached_votes_total"], name: "index_debates_on_cached_votes_total", using: :btree add_index "debates", ["cached_votes_up"], name: "index_debates_on_cached_votes_up", using: :btree add_index "debates", ["confidence_score"], name: "index_debates_on_confidence_score", using: :btree + add_index "debates", ["description"], name: "index_debates_on_description", using: :btree add_index "debates", ["geozone_id"], name: "index_debates_on_geozone_id", using: :btree add_index "debates", ["hidden_at"], name: "index_debates_on_hidden_at", using: :btree add_index "debates", ["hot_score"], name: "index_debates_on_hot_score", using: :btree @@ -205,7 +206,7 @@ ActiveRecord::Schema.define(version: 20160222145100) do create_table "locks", force: :cascade do |t| t.integer "user_id" t.integer "tries", default: 0 - t.datetime "locked_until", default: '2000-01-01 07:01:01', null: false + t.datetime "locked_until", default: '2000-01-01 00:01:01', null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end @@ -265,6 +266,7 @@ ActiveRecord::Schema.define(version: 20160222145100) do add_index "proposals", ["author_id"], name: "index_proposals_on_author_id", using: :btree add_index "proposals", ["cached_votes_up"], name: "index_proposals_on_cached_votes_up", using: :btree add_index "proposals", ["confidence_score"], name: "index_proposals_on_confidence_score", using: :btree + add_index "proposals", ["description"], name: "index_proposals_on_description", using: :btree add_index "proposals", ["geozone_id"], name: "index_proposals_on_geozone_id", using: :btree add_index "proposals", ["hidden_at"], name: "index_proposals_on_hidden_at", using: :btree add_index "proposals", ["hot_score"], name: "index_proposals_on_hot_score", using: :btree @@ -294,20 +296,22 @@ ActiveRecord::Schema.define(version: 20160222145100) do t.text "description" t.integer "author_id" t.string "external_url" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "geozone_id" - t.string "resolution" t.float "price" - t.boolean "legal" t.boolean "feasible" - t.text "explanation" t.string "association_name" + t.text "price_explanation" + t.text "feasible_explanation" + t.text "internal_comments" + t.boolean "valuation_finished", default: false + t.text "explanations_log" + t.integer "administrator_id" end add_index "spending_proposals", ["author_id"], name: "index_spending_proposals_on_author_id", using: :btree add_index "spending_proposals", ["geozone_id"], name: "index_spending_proposals_on_geozone_id", using: :btree - add_index "spending_proposals", ["resolution"], name: "index_spending_proposals_on_resolution", using: :btree create_table "taggings", force: :cascade do |t| t.integer "tag_id" @@ -328,8 +332,8 @@ ActiveRecord::Schema.define(version: 20160222145100) do t.boolean "featured", default: false t.integer "debates_count", default: 0 t.integer "proposals_count", default: 0 - t.string "kind" t.integer "spending_proposals_count", default: 0 + t.string "kind" end add_index "tags", ["debates_count"], name: "index_tags_on_debates_count", using: :btree @@ -405,8 +409,8 @@ ActiveRecord::Schema.define(version: 20160222145100) do t.boolean "public_activity", default: true t.boolean "newsletter", default: false t.integer "notifications_count", default: 0 - t.string "locale" t.boolean "registering_with_oauth", default: false + t.string "locale" t.string "oauth_email" t.integer "geozone_id" t.string "redeemable_code" @@ -418,6 +422,13 @@ ActiveRecord::Schema.define(version: 20160222145100) do add_index "users", ["hidden_at"], name: "index_users_on_hidden_at", using: :btree add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree + create_table "valuation_assignments", force: :cascade do |t| + t.integer "valuator_id" + t.integer "spending_proposal_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "valuators", force: :cascade do |t| t.integer "user_id" end diff --git a/spec/factories.rb b/spec/factories.rb index 0e5831556..6cfe2779b 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1,4 +1,5 @@ FactoryGirl.define do + sequence(:document_number) { |n| "#{n.to_s.rjust(8, '0')}X" } factory :user do diff --git a/spec/features/admin/spending_proposals_spec.rb b/spec/features/admin/spending_proposals_spec.rb index 3b619dc3e..a3a844d94 100644 --- a/spec/features/admin/spending_proposals_spec.rb +++ b/spec/features/admin/spending_proposals_spec.rb @@ -19,97 +19,18 @@ feature 'Admin spending proposals' do expect(page).to have_content(spending_proposal.title) end - scenario 'Accept from index' do - spending_proposal = create(:spending_proposal) - visit admin_spending_proposals_path - - click_link 'Accept' - - expect(page).to_not have_content(spending_proposal.title) - - click_link 'Accepted' - expect(page).to have_content(spending_proposal.title) - - expect(spending_proposal.reload).to be_accepted - end - - scenario 'Reject from index' do - spending_proposal = create(:spending_proposal) - visit admin_spending_proposals_path - - click_link 'Reject' - - expect(page).to_not have_content(spending_proposal.title) - - click_link('Rejected') - expect(page).to have_content(spending_proposal.title) - - expect(spending_proposal.reload).to be_rejected - end - - scenario "Current filter is properly highlighted" do - visit admin_spending_proposals_path - expect(page).to_not have_link('Unresolved') - expect(page).to have_link('Accepted') - expect(page).to have_link('Rejected') - - visit admin_spending_proposals_path(filter: 'unresolved') - expect(page).to_not have_link('Unresolved') - expect(page).to have_link('Accepted') - expect(page).to have_link('Rejected') - - visit admin_spending_proposals_path(filter: 'accepted') - expect(page).to have_link('Unresolved') - expect(page).to_not have_link('Accepted') - expect(page).to have_link('Rejected') - - visit admin_spending_proposals_path(filter: 'rejected') - expect(page).to have_link('Accepted') - expect(page).to have_link('Unresolved') - expect(page).to_not have_link('Rejected') - end - - scenario "Filtering proposals" do - create(:spending_proposal, title: "Recent spending proposal") - create(:spending_proposal, title: "Good spending proposal", resolution: "accepted") - create(:spending_proposal, title: "Bad spending proposal", resolution: "rejected") - - visit admin_spending_proposals_path(filter: 'unresolved') - expect(page).to have_content('Recent spending proposal') - expect(page).to_not have_content('Good spending proposal') - expect(page).to_not have_content('Bad spending proposal') - - visit admin_spending_proposals_path(filter: 'accepted') - expect(page).to have_content('Good spending proposal') - expect(page).to_not have_content('Recent spending proposal') - expect(page).to_not have_content('Bad spending proposal') - - visit admin_spending_proposals_path(filter: 'rejected') - expect(page).to have_content('Bad spending proposal') - expect(page).to_not have_content('Good spending proposal') - expect(page).to_not have_content('Recent spending proposal') - end - - scenario "Action links remember the pagination setting and the filter" do - per_page = Kaminari.config.default_per_page - (per_page + 2).times { create(:spending_proposal, resolution: "accepted") } - - visit admin_spending_proposals_path(filter: 'accepted', page: 2) - - click_on('Reject', match: :first, exact: true) - - expect(current_url).to include('filter=accepted') - expect(current_url).to include('page=2') - 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')) spending_proposal = create(:spending_proposal, geozone: create(:geozone), association_name: 'People of the neighbourhood', price: 1234.56, - legal: true, feasible: false, - explanation: "It's impossible") + feasible_explanation: 'It is impossible', + administrator: administrator) + spending_proposal.valuators << valuator + visit admin_spending_proposals_path click_link spending_proposal.title @@ -119,38 +40,71 @@ feature 'Admin spending proposals' do expect(page).to have_content(spending_proposal.author.name) expect(page).to have_content(spending_proposal.association_name) expect(page).to have_content(spending_proposal.geozone.name) - expect(page).to have_content("1234.56") - expect(page).to have_content("Legal") - expect(page).to have_content("Not feasible") - expect(page).to have_content("It's impossible") + expect(page).to have_content('1234.56') + expect(page).to have_content('Not feasible') + expect(page).to have_content('It is impossible') + expect(page).to have_select('spending_proposal[administrator_id]', selected: 'Ana (ana@admins.org)') + + within('#assigned_valuators') do + expect(page).to have_content('Rachel (rachel@valuators.org)') + end end - scenario 'Accept from show' do + scenario 'Administrator assigment', :js do spending_proposal = create(:spending_proposal) + + administrator = create(:administrator, user: create(:user, username: 'Ana', email: 'ana@admins.org')) + visit admin_spending_proposal_path(spending_proposal) - click_link 'Accept' + expect(page).to have_select('spending_proposal[administrator_id]', selected: 'Undefined') + select 'Ana (ana@admins.org)', from: 'spending_proposal[administrator_id]' - expect(page).to_not have_content(spending_proposal.title) + visit admin_spending_proposal_path(spending_proposal) - click_link 'Accepted' - expect(page).to have_content(spending_proposal.title) - - expect(spending_proposal.reload).to be_accepted + expect(page).to have_select('spending_proposal[administrator_id]', selected: 'Ana (ana@admins.org)') end - scenario 'Reject from show' do + scenario 'Valuators assigments', :js do spending_proposal = create(:spending_proposal) + + 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_spending_proposal_path(spending_proposal) - click_link 'Reject' + within('#assigned_valuators') do + expect(page).to have_content('Undefined') + expect(page).to_not have_content('Valentina (v1@valuators.org)') + expect(page).to_not have_content('Valerian (v2@valuators.org)') + expect(page).to_not have_content('Val (v3@valuators.org)') + end - expect(page).to_not have_content(spending_proposal.title) + visit admin_spending_proposal_path(spending_proposal) - click_link('Rejected') - expect(page).to have_content(spending_proposal.title) + click_link "Assign valuators" - expect(spending_proposal.reload).to be_rejected + within('#valuators-assign-list') do + check "valuator_ids_#{valuator1.id}" + check "valuator_ids_#{valuator3.id}" + end + + 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 + + visit admin_spending_proposal_path(spending_proposal) + + 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 end diff --git a/spec/models/spending_proposal_spec.rb b/spec/models/spending_proposal_spec.rb index 3d12b9489..d0b6973a4 100644 --- a/spec/models/spending_proposal_spec.rb +++ b/spec/models/spending_proposal_spec.rb @@ -43,23 +43,6 @@ describe SpendingProposal do end describe "dossier info" do - describe "#legality" do - it "can be legal" do - spending_proposal.legal = true - expect(spending_proposal.legality).to eq "legal" - end - - it "can be not-legal" do - spending_proposal.legal = false - expect(spending_proposal.legality).to eq "not_legal" - end - - it "can be undefined" do - spending_proposal.legal = nil - expect(spending_proposal.legality).to eq "undefined" - end - end - describe "#feasibility" do it "can be feasible" do spending_proposal.feasible = true @@ -78,99 +61,4 @@ describe SpendingProposal do end end - describe "resolution status" do - it "should be valid" do - spending_proposal.resolution = "accepted" - expect(spending_proposal).to be_valid - spending_proposal.resolution = "rejected" - expect(spending_proposal).to be_valid - spending_proposal.resolution = "wrong" - expect(spending_proposal).to_not be_valid - end - - it "can be accepted" do - spending_proposal.accept - expect(spending_proposal.reload.resolution).to eq("accepted") - end - - it "can be rejected" do - spending_proposal.reject - expect(spending_proposal.reload.resolution).to eq("rejected") - end - - describe "#accepted?" do - it "should be true if resolution equals 'accepted'" do - spending_proposal.resolution = "accepted" - expect(spending_proposal.accepted?).to eq true - end - - it "should be false otherwise" do - spending_proposal.resolution = "rejected" - expect(spending_proposal.accepted?).to eq false - spending_proposal.resolution = nil - expect(spending_proposal.accepted?).to eq false - end - end - - describe "#rejected?" do - it "should be true if resolution equals 'rejected'" do - spending_proposal.resolution = "rejected" - expect(spending_proposal.rejected?).to eq true - end - - it "should be false otherwise" do - spending_proposal.resolution = "accepted" - expect(spending_proposal.rejected?).to eq false - spending_proposal.resolution = nil - expect(spending_proposal.rejected?).to eq false - end - end - - describe "#unresolved?" do - it "should be true if resolution is blank" do - spending_proposal.resolution = nil - expect(spending_proposal.unresolved?).to eq true - end - - it "should be false otherwise" do - spending_proposal.resolution = "accepted" - expect(spending_proposal.unresolved?).to eq false - spending_proposal.resolution = "rejected" - expect(spending_proposal.unresolved?).to eq false - end - end - end - - describe "scopes" do - before(:each) do - 2.times { create(:spending_proposal, resolution: "accepted") } - 2.times { create(:spending_proposal, resolution: "rejected") } - 2.times { create(:spending_proposal, resolution: nil) } - end - - describe "unresolved" do - it "should return all spending proposals without resolution" do - unresolved = SpendingProposal.all.unresolved - expect(unresolved.size).to eq(2) - unresolved.each {|u| expect(u.resolution).to be_nil} - end - end - - describe "accepted" do - it "should return all accepted spending proposals" do - accepted = SpendingProposal.all.accepted - expect(accepted.size).to eq(2) - accepted.each {|a| expect(a.resolution).to eq("accepted")} - end - end - - describe "rejected" do - it "should return all rejected spending proposals" do - rejected = SpendingProposal.all.rejected - expect(rejected.size).to eq(2) - rejected.each {|r| expect(r.resolution).to eq("rejected")} - end - end - end - end