diff --git a/.travis.yml b/.travis.yml index 0aa683c04..f9222fdb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,10 +22,11 @@ env: global: - KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true - KNAPSACK_PRO_LOG_LEVEL=info - - KNAPSACK_PRO_CI_NODE_TOTAL=2 + - KNAPSACK_PRO_CI_NODE_TOTAL=3 matrix: - KNAPSACK_PRO_CI_NODE_INDEX=0 - KNAPSACK_PRO_CI_NODE_INDEX=1 + - KNAPSACK_PRO_CI_NODE_INDEX=2 notifications: slack: secure: 18E9SU0SR/9knRvCMYwVqFCqVTBT6qJtZQ/gadpheqUPPlcLoQfnlIzJkLIYqkE0sn1nkBE5Bt2I90FU53p0NkrTEmSGlQXcN1vEXM8EXMaoVf3NBsIJeleMwt9VTojzo81EgIi6x7q3fDiFORJ4rqOGd9XkeLn5yrAtIkdaenVs0bhS5s24FP76hKqO37IFLG2v3EEqxg5k31oW6yhyP35Mxns+AGbfaZbxEy4XbCoU65KFuYhBsVZ/y1evOl/wcre2fCAoT2uKeqUWGEcDzH7oSCz7vfk7iO9BZnO++v7oj8mr/nrZL1KMFt77eqtdT51XQoJcchgJC/R9km5hRGkQqFCHhqPcBxo5c3p+jauL0kLaqTggeLDv2FQ2huJ8FSJ4ADac+n3g7wT7BX7HJlCvK0nbooY1JtBlk7+6/pw6ksSFIOo0FHg5gXN9IlG1tQQuENzzsXULNc6s4nPeT+n78uOp1b0N/Gn06moEBaKgXqqx1yV1XeJ02X8n3uDZxPuX3n2bJ4DMIrBjeWApxHAgyOraOzQHNQgJoj4tHlWutF33ApV2tcIMefIzvjM4tIYwIkpfGgohGaTf8eU5X9pqiMgwlDpJHVBsSvpk/Z/Nj7evYznjBiDYqOcXoztsqHrS0C91MaT+eExDfd9HDmThsE07RT7zcP9aElFZA/k= diff --git a/README.md b/README.md index 9cb539e31..37adc5791 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ + + ![CONSUL logo](https://raw.githubusercontent.com/consul/consul/master/public/consul_logo.png) # CONSUL @@ -19,13 +25,17 @@ Citizen Participation and Open Government Application This is the opensource code repository of the eParticipation website CONSUL, originally developed for the Madrid City government eParticipation website -## Current state +## Documentation -Development started on [2015 July 15th](https://github.com/consul/consul/commit/8db36308379accd44b5de4f680a54c41a0cc6fc6). Code was deployed to production on 2015 september 7th to [decide.madrid.es](https://decide.madrid.es). Since then new features are added often. You can take a look at the current features at the [project's website](http://consulproject.org/) and future features at the [Roadmap](https://github.com/consul/consul/projects/6) and [open issues list](https://github.com/consul/consul/issues). +Check the ongoing documentation at [https://docs.consulproject.org](https://docs.consulproject.org) to learn more about how to start your own CONSUL fork, install it, customize it and learn to use it from an administrator/maintainer perspective. + +## CONSUL Project main website + +You can access the main website of the project at [http://consulproject.org](http://consulproject.org) where you can find documentation about the use of the platform, videos, and links to the community space. ## Configuration for development and test environments -**NOTE**: For more detailed instructions check the [docs](https://consul_docs.gitbooks.io/docs/) +**NOTE**: For more detailed instructions check the [docs](https://docs.consulproject.org) Prerequisites: install git, Ruby 2.3.2, `bundler` gem, Node.js and PostgreSQL (>=9.4). @@ -69,9 +79,9 @@ But for some actions like voting, you will need a verified user, the seeds file See [installer](https://github.com/consul/installer) -## Documentation +## Current state -Check the ongoing documentation at [https://consul_docs.gitbooks.io/docs/content/](https://consul_docs.gitbooks.io/docs/content/) to learn more about how to start your own CONSUL fork, install it, customize it and learn to use it from an administrator/maintainer perspective. You can contribute to it at [https://github.com/consul/docs](https://github.com/consul/docs) +Development started on [2015 July 15th](https://github.com/consul/consul/commit/8db36308379accd44b5de4f680a54c41a0cc6fc6). Code was deployed to production on 2015 september 7th to [decide.madrid.es](https://decide.madrid.es). Since then new features are added often. You can take a look at the current features at the [project's website](http://consulproject.org/) and future features at the [Roadmap](https://github.com/consul/consul/projects/6) and [open issues list](https://github.com/consul/consul/issues). ## License diff --git a/README_ES.md b/README_ES.md index ee5f6d16b..052b4ee85 100644 --- a/README_ES.md +++ b/README_ES.md @@ -1,3 +1,9 @@ + + ![Logotipo de CONSUL](https://raw.githubusercontent.com/consul/consul/master/public/consul_logo.png) # CONSUL @@ -18,13 +24,17 @@ Aplicación de Participación Ciudadana y Gobierno Abierto Este es el repositorio de código abierto de la Aplicación de Participación Ciudadana CONSUL, creada originariamente por el Ayuntamiento de Madrid. -## Estado del proyecto +## Documentación -El desarrollo de esta aplicación comenzó el [15 de Julio de 2015](https://github.com/consul/consul/commit/8db36308379accd44b5de4f680a54c41a0cc6fc6) y el código fue puesto en producción el día 7 de Septiembre de 2015 en [decide.madrid.es](https://decide.madrid.es). Desde entonces se le añaden mejoras y funcionalidades constantemente. Las funcionalidades actuales se pueden consultar en la [la página del projecto](http://consulproject.org/es) y las futuras funcionalidades en el [Roadmap](https://github.com/consul/consul/projects/6) y [el listado de issues](https://github.com/consul/consul/issues). +Por favor visita la documentación que está siendo completada en [https://docs.consulproject.org](https://docs.consulproject.org) para conocer más sobre este proyecto, cómo comenzar tu propio fork, instalarlo, personalizarlo y usarlo como administrador/mantenedor. + +## Web CONSUL Project + +Puedes acceder a la página principal del proyecto en [http://consulproject.org](http://consulproject.org) donde puedes encontrar documentación sobre el uso de la plataforma, videos y enlaces al espacio de la comunidad. ## Configuración para desarrollo y tests -**NOTA**: para unas instrucciones más detalladas consulta la [documentación](https://github.com/consul/docs/tree/master/es/getting_started/prerequisites) +**NOTA**: para unas instrucciones más detalladas consulta la [documentación](https://docs.consulproject.org) Prerequisitos: tener instalado git, Ruby 2.3.2, la gema `bundler`, Node.js y PostgreSQL (9.4 o superior). @@ -64,9 +74,9 @@ Pero para ciertas acciones, como apoyar, necesitarás un usuario verificado, el **user:** verified@consul.dev **pass:** 12345678 -## Documentación +## Estado del proyecto -Por favor visita la documentación que está siendo completada en [https://consul_docs.gitbooks.io/docs/content/](https://consul_docs.gitbooks.io/docs/content/) para conocer más sobre este proyecto, como comenzar tu propio fork, instalarlo, customizarlo y usarlo como administrador/mantenedor. Puedes colaborar en ella en [https://github.com/consul/docs](https://github.com/consul/docs) +El desarrollo de esta aplicación comenzó el [15 de Julio de 2015](https://github.com/consul/consul/commit/8db36308379accd44b5de4f680a54c41a0cc6fc6) y el código fue puesto en producción el día 7 de Septiembre de 2015 en [decide.madrid.es](https://decide.madrid.es). Desde entonces se le añaden mejoras y funcionalidades constantemente. Las funcionalidades actuales se pueden consultar en la [la página del projecto](http://consulproject.org/es) y las futuras funcionalidades en el [Roadmap](https://github.com/consul/consul/projects/6) y [el listado de issues](https://github.com/consul/consul/issues). ## Licencia diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 17ea18b9d..9c95f400f 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -1205,6 +1205,11 @@ table { .filter { display: inline-block; margin: 0 $line-height / 2; + + label { + font-weight: normal; + margin: 0; + } } .button { diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index acbe5895b..2e35b8256 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -1168,6 +1168,11 @@ text-decoration: none; } + .confirmed { + font-size: rem-calc(24); + font-weight: bold; + } + .info { background: #6a2a72; diff --git a/app/controllers/admin/budget_investments_controller.rb b/app/controllers/admin/budget_investments_controller.rb index 1a213c2f2..a451d67a5 100644 --- a/app/controllers/admin/budget_investments_controller.rb +++ b/app/controllers/admin/budget_investments_controller.rb @@ -4,10 +4,8 @@ class Admin::BudgetInvestmentsController < Admin::BaseController feature_flag :budgets - has_orders %w{oldest}, only: [:show, :edit] - has_filters(%w{all without_admin without_valuator under_valuation - valuation_finished winners}, - only: [:index, :toggle_selection]) + has_orders %w[oldest], only: [:show, :edit] + has_filters %w[all], only: [:index, :toggle_selection] before_action :load_budget before_action :load_investment, only: [:show, :edit, :update, :toggle_selection] diff --git a/app/controllers/admin/budgets_controller.rb b/app/controllers/admin/budgets_controller.rb index 252e48134..955da3f1d 100644 --- a/app/controllers/admin/budgets_controller.rb +++ b/app/controllers/admin/budgets_controller.rb @@ -23,7 +23,9 @@ class Admin::BudgetsController < Admin::BaseController def calculate_winners return unless @budget.balloting_process? @budget.headings.each { |heading| Budget::Result.new(@budget, heading).delay.calculate_winners } - redirect_to admin_budget_budget_investments_path(budget_id: @budget.id, filter: "winners"), + redirect_to admin_budget_budget_investments_path( + budget_id: @budget.id, + advanced_filters: ["winners"]), notice: I18n.t("admin.budgets.winners.calculated") end diff --git a/app/controllers/officing/base_controller.rb b/app/controllers/officing/base_controller.rb index 48054ce6a..58ba3c8b2 100644 --- a/app/controllers/officing/base_controller.rb +++ b/app/controllers/officing/base_controller.rb @@ -1,12 +1,46 @@ class Officing::BaseController < ApplicationController layout "admin" + helper_method :current_booth before_action :authenticate_user! before_action :verify_officer skip_authorization_check - def verify_officer - raise CanCan::AccessDenied unless current_user.try(:poll_officer?) - end + private + + def verify_officer + raise CanCan::AccessDenied unless current_user.try(:poll_officer?) + end + + def load_officer_assignment + @officer_assignments ||= current_user.poll_officer. + officer_assignments. + voting_days. + where(date: Time.current.to_date) + end + + def verify_officer_assignment + if @officer_assignments.blank? + redirect_to officing_root_path, notice: t("officing.residence.flash.not_allowed") + end + end + + def verify_booth + return unless current_booth.blank? + booths = current_user.poll_officer.todays_booths + case booths.count + when 0 + redirect_to officing_root_path + when 1 + session[:booth_id] = booths.first.id + else + redirect_to new_officing_booth_path + end + end + + def current_booth + Poll::Booth.where(id: session[:booth_id]).first + end + end diff --git a/app/controllers/officing/booth_controller.rb b/app/controllers/officing/booth_controller.rb new file mode 100644 index 000000000..584c92de0 --- /dev/null +++ b/app/controllers/officing/booth_controller.rb @@ -0,0 +1,24 @@ +class Officing::BoothController < Officing::BaseController + before_action :load_officer_assignment + before_action :verify_officer_assignment + + def new + @booths = current_user.poll_officer.todays_booths + end + + def create + set_booth(Poll::Booth.find(booth_params[:id])) + redirect_to officing_root_path + end + + private + + def booth_params + params.require(:booth).permit(:id) + end + + def set_booth(booth) + session[:booth_id] = booth.id + end + +end diff --git a/app/controllers/officing/polls_controller.rb b/app/controllers/officing/polls_controller.rb index 69b65ad23..dc806d35f 100644 --- a/app/controllers/officing/polls_controller.rb +++ b/app/controllers/officing/polls_controller.rb @@ -1,4 +1,5 @@ class Officing::PollsController < Officing::BaseController + before_action :verify_booth def index @polls = current_user.poll_officer? ? current_user.poll_officer.voting_days_assigned_polls : [] diff --git a/app/controllers/officing/residence_controller.rb b/app/controllers/officing/residence_controller.rb index ef76162ea..cb7b96a64 100644 --- a/app/controllers/officing/residence_controller.rb +++ b/app/controllers/officing/residence_controller.rb @@ -1,7 +1,8 @@ class Officing::ResidenceController < Officing::BaseController before_action :load_officer_assignment - before_action :validate_officer_assignment, only: :create + before_action :verify_officer_assignment + before_action :verify_booth def new @residence = Officing::Residence.new @@ -22,16 +23,4 @@ class Officing::ResidenceController < Officing::BaseController params.require(:residence).permit(:document_number, :document_type, :year_of_birth) end - def load_officer_assignment - @officer_assignments = current_user.poll_officer. - officer_assignments. - voting_days. - where(date: Date.current) - end - - def validate_officer_assignment - if @officer_assignments.blank? - redirect_to officing_root_path, notice: t("officing.residence.flash.not_allowed") - end - end end diff --git a/app/controllers/officing/results_controller.rb b/app/controllers/officing/results_controller.rb index 54bb1076c..ff9bf3925 100644 --- a/app/controllers/officing/results_controller.rb +++ b/app/controllers/officing/results_controller.rb @@ -7,6 +7,7 @@ class Officing::ResultsController < Officing::BaseController before_action :load_officer_assignment, only: :create before_action :check_officer_assignment, only: :create before_action :build_results, only: :create + before_action :verify_booth def new end diff --git a/app/controllers/officing/voters_controller.rb b/app/controllers/officing/voters_controller.rb index 2b7ed329f..78233d41c 100644 --- a/app/controllers/officing/voters_controller.rb +++ b/app/controllers/officing/voters_controller.rb @@ -1,6 +1,10 @@ class Officing::VotersController < Officing::BaseController respond_to :html, :js + before_action :load_officer_assignment + before_action :verify_officer_assignment + before_action :verify_booth + def new @user = User.find(params[:id]) booths = current_user.poll_officer.shifts.current.vote_collection.pluck(:booth_id).uniq @@ -15,7 +19,9 @@ class Officing::VotersController < Officing::BaseController user: @user, poll: @poll, origin: "booth", - officer: current_user.poll_officer) + officer: current_user.poll_officer, + booth_assignment: Poll::BoothAssignment.where(poll: @poll, booth: current_booth).first, + officer_assignment: officer_assignment(@poll)) @voter.save! end @@ -25,4 +31,13 @@ class Officing::VotersController < Officing::BaseController params.require(:voter).permit(:poll_id, :user_id) end + def officer_assignment(poll) + Poll::OfficerAssignment.by_officer(current_user.poll_officer) + .by_poll(poll) + .by_booth(current_booth) + .by_date(Date.current) + .where(final: false) + .first + end + end diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index d16f9c87e..0409a9d5e 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -3,7 +3,9 @@ class Users::SessionsController < Devise::SessionsController private def after_sign_in_path_for(resource) - if !verifying_via_email? && resource.show_welcome_screen? + if current_user.poll_officer? + new_officing_booth_path + elsif !verifying_via_email? && resource.show_welcome_screen? welcome_path else super diff --git a/app/helpers/admin_budget_investments_helper.rb b/app/helpers/admin_budget_investments_helper.rb index 5c2dc479e..aa33961aa 100644 --- a/app/helpers/admin_budget_investments_helper.rb +++ b/app/helpers/admin_budget_investments_helper.rb @@ -1,7 +1,13 @@ module AdminBudgetInvestmentsHelper def advanced_menu_visibility - (params[:advanced_filters].empty? && params["min_total_supports"].blank?) ? "hide" : "" + if params[:advanced_filters].empty? && + params["min_total_supports"].blank? && + params["max_total_supports"].blank? + "hide" + else + "" + end end def init_advanced_menu diff --git a/app/helpers/officers_helper.rb b/app/helpers/officers_helper.rb index e069cb9aa..1218af80e 100644 --- a/app/helpers/officers_helper.rb +++ b/app/helpers/officers_helper.rb @@ -5,11 +5,15 @@ module OfficersHelper end def vote_collection_shift? - current_user.poll_officer.officer_assignments.where(date: Time.current.to_date).any? + current_user.poll_officer.officer_assignments.voting_days.where(date: Time.current.to_date).any? end def final_recount_shift? current_user.poll_officer.officer_assignments.final.where(date: Time.current.to_date).any? end + def no_shifts? + current_user.poll_officer.officer_assignments.where(date: Time.current.to_date).blank? + end + end diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index f2095c746..b033d7404 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -117,7 +117,9 @@ class Budget results = Investment.by_budget(budget) results = results.where("cached_votes_up + physical_votes >= ?", - params[:min_total_supports]) if params[:min_total_supports].present? + params[:min_total_supports]) if params[:min_total_supports].present? + results = results.where("cached_votes_up + physical_votes <= ?", + params[:max_total_supports]) if params[:max_total_supports].present? results = results.where(group_id: params[:group_id]) if params[:group_id].present? results = results.by_tag(params[:tag_name]) if params[:tag_name].present? results = results.by_heading(params[:heading_id]) if params[:heading_id].present? @@ -132,12 +134,19 @@ class Budget end def self.advanced_filters(params, results) + results = results.without_admin if params[:advanced_filters].include?("without_admin") + results = results.without_valuator if params[:advanced_filters].include?("without_valuator") + results = results.under_valuation if params[:advanced_filters].include?("under_valuation") + results = results.valuation_finished if params[:advanced_filters].include?("valuation_finished") + results = results.winners if params[:advanced_filters].include?("winners") + ids = [] ids += results.valuation_finished_feasible.pluck(:id) if params[:advanced_filters].include?("feasible") ids += results.where(selected: true).pluck(:id) if params[:advanced_filters].include?("selected") ids += results.undecided.pluck(:id) if params[:advanced_filters].include?("undecided") ids += results.unfeasible.pluck(:id) if params[:advanced_filters].include?("unfeasible") - results.where("budget_investments.id IN (?)", ids) + results = results.where("budget_investments.id IN (?)", ids) if ids.any? + results end def self.order_filter(params) diff --git a/app/models/budget/investment/exporter.rb b/app/models/budget/investment/exporter.rb index ec5b6abf4..e355600f3 100644 --- a/app/models/budget/investment/exporter.rb +++ b/app/models/budget/investment/exporter.rb @@ -58,6 +58,10 @@ class Budget::Investment::Exporter def price(investment) price_string = "admin.budget_investments.index.feasibility.#{investment.feasibility}" - I18n.t(price_string, price: investment.formatted_price) + if investment.feasible? + "#{I18n.t(price_string)} (#{investment.formatted_price})" + else + I18n.t(price_string) + end end end diff --git a/app/models/poll/officer.rb b/app/models/poll/officer.rb index 384202455..ef96cfa91 100644 --- a/app/models/poll/officer.rb +++ b/app/models/poll/officer.rb @@ -23,5 +23,9 @@ class Poll sort {|x, y| y.ends_at <=> x.ends_at} end + def todays_booths + officer_assignments.by_date(Date.current).map(&:booth).uniq + end + end end diff --git a/app/models/poll/officer_assignment.rb b/app/models/poll/officer_assignment.rb index bdd074995..7de4f98b3 100644 --- a/app/models/poll/officer_assignment.rb +++ b/app/models/poll/officer_assignment.rb @@ -17,11 +17,20 @@ class Poll scope :by_officer_and_poll, ->(officer_id, poll_id) do where("officer_id = ? AND poll_booth_assignments.poll_id = ?", officer_id, poll_id) end + scope :by_officer, ->(officer){ where(officer_id: officer.id) } + scope :by_poll, ->(poll){ joins(:booth_assignment).where("poll_booth_assignments.poll_id" => poll.id) } + scope :by_booth, ->(booth){ joins(:booth_assignment).where("poll_booth_assignments.booth_id" => booth.id) } + scope :by_date, ->(date){ where(date: date) } before_create :log_user_data def log_user_data self.user_data_log = "#{officer.user_id} - #{officer.user.name_and_email}" end + + def booth + booth_assignment.booth + end + end end diff --git a/app/models/poll/voter.rb b/app/models/poll/voter.rb index 0826092d2..1e4a0d204 100644 --- a/app/models/poll/voter.rb +++ b/app/models/poll/voter.rb @@ -12,11 +12,13 @@ class Poll validates :poll_id, presence: true validates :user_id, presence: true + validates :booth_assignment_id, presence: true, if: ->(voter) { voter.origin == "booth" } + validates :officer_assignment_id, presence: true, if: ->(voter) { voter.origin == "booth" } validates :document_number, presence: true, uniqueness: { scope: [:poll_id, :document_type], message: :has_voted } validates :origin, inclusion: { in: VALID_ORIGINS } - before_validation :set_demographic_info, :set_document_info + before_validation :set_demographic_info, :set_document_info, :set_denormalized_booth_assignment_id scope :web, -> { where(origin: "web") } scope :booth, -> { where(origin: "booth") } @@ -38,6 +40,10 @@ class Poll private + def set_denormalized_booth_assignment_id + self.booth_assignment_id ||= officer_assignment.try(:booth_assignment_id) + end + def in_census? census_api_response.valid? end diff --git a/app/views/admin/budget_investments/_investments.html.erb b/app/views/admin/budget_investments/_investments.html.erb index 0be6f5e10..55603bbcd 100644 --- a/app/views/admin/budget_investments/_investments.html.erb +++ b/app/views/admin/budget_investments/_investments.html.erb @@ -2,7 +2,7 @@ admin_budget_budget_investments_path(csv_params), class: "float-right small clear" %> -<% if params[:filter] == "winners" %> +<% if params[:advanced_filters].include?("winners") %> <% if display_calculate_winners_button?(@budget) %> <%= link_to calculate_winner_button_text(@budget), calculate_winners_admin_budget_path(@budget), @@ -36,6 +36,7 @@ <%= t("admin.budget_investments.index.list.geozone") %> <%= t("admin.budget_investments.index.list.feasibility") %> + <%= t("admin.budget_investments.index.list.price") %> <%= t("admin.budget_investments.index.list.valuation_finished") %> <%= t("admin.budget_investments.index.list.visible_to_valuators") %> <%= t("admin.budget_investments.index.list.selected") %> diff --git a/app/views/admin/budget_investments/_search_form.html.erb b/app/views/admin/budget_investments/_search_form.html.erb index 94ae8fc0b..301845a8b 100644 --- a/app/views/admin/budget_investments/_search_form.html.erb +++ b/app/views/admin/budget_investments/_search_form.html.erb @@ -11,14 +11,22 @@
- <% ["feasible", "selected", "undecided", "unfeasible"].each do |option| %> + <% %w[feasible selected undecided unfeasible without_admin without_valuator under_valuation + valuation_finished winners].each do |filter| %>
- <%= check_box_tag "advanced_filters[]", option, params[:advanced_filters].index(option), id: "advanced_filters_#{option}" %> - <%= t("admin.budget_investments.index.filters.#{option}") %> + <%= check_box_tag "advanced_filters[]", filter, params[:advanced_filters].index(filter), id: "advanced_filters_#{filter}" %> + <%= label_tag "advanced_filters[#{filter}]", t("admin.budget_investments.index.filters.#{filter}") %>
<% end %> -
- <%= text_field_tag :min_total_supports, params["min_total_supports"], placeholder: t("admin.budget_investments.index.filters.min_total_supports") %> +
+
+ <%= label_tag :min_total_supports, t("admin.budget_investments.index.filters.min_total_supports") %> + <%= text_field_tag :min_total_supports, params["min_total_supports"] %> +
+
+ <%= label_tag :max_total_supports, t("admin.budget_investments.index.filters.max_total_supports") %> + <%= text_field_tag :max_total_supports, params["max_total_supports"] %> +
diff --git a/app/views/admin/budget_investments/_select_investment.html.erb b/app/views/admin/budget_investments/_select_investment.html.erb index 12bb5c5c8..e4c7f5355 100644 --- a/app/views/admin/budget_investments/_select_investment.html.erb +++ b/app/views/admin/budget_investments/_select_investment.html.erb @@ -31,8 +31,10 @@ <%= investment.heading.name %> - <%= t("admin.budget_investments.index.feasibility.#{investment.feasibility}", - price: investment.formatted_price) %> + <%= t("admin.budget_investments.index.feasibility.#{investment.feasibility}") %> + + + <%= investment.formatted_price %> <%= investment.valuation_finished? ? t("shared.yes"): t("shared.no") %> @@ -54,6 +56,7 @@ filter: params[:filter], sort_by: params[:sort_by], min_total_supports: params[:min_total_supports], + max_total_supports: params[:max_total_supports], advanced_filters: params[:advanced_filters], page: params[:page]), method: :patch, @@ -67,6 +70,7 @@ filter: params[:filter], sort_by: params[:sort_by], min_total_supports: params[:min_total_supports], + max_total_supports: params[:max_total_supports], advanced_filters: params[:advanced_filters], page: params[:page]), method: :patch, diff --git a/app/views/admin/poll/recounts/index.html.erb b/app/views/admin/poll/recounts/index.html.erb index 692078340..5a9766e94 100644 --- a/app/views/admin/poll/recounts/index.html.erb +++ b/app/views/admin/poll/recounts/index.html.erb @@ -43,14 +43,14 @@ <%= link_to booth_assignment.booth.name, admin_poll_booth_assignment_path(@poll, booth_assignment, anchor: "tab-recounts") %> - "> + " id="<%= dom_id(booth_assignment) %>_recount"> <% if total_recounts.present? %> <%= total_recounts %> <% else %> - <% end %> - + <% if system_count.present? %> <%= system_count %> <% else %> diff --git a/app/views/budgets/ballot/_ballot.html.erb b/app/views/budgets/ballot/_ballot.html.erb index 5dd8bed23..e99de7510 100644 --- a/app/views/budgets/ballot/_ballot.html.erb +++ b/app/views/budgets/ballot/_ballot.html.erb @@ -9,11 +9,10 @@ <%= t("budgets.ballots.show.voted_html", count: @ballot.investments.count) %> +

+ <%= t("budgets.ballots.show.voted_info_html") %>

- - <%= t("budgets.ballots.show.voted_info_html") %> - -

+

<%= t("budgets.ballots.show.voted_info_2") %>

diff --git a/app/views/budgets/investments/_sidebar.html.erb b/app/views/budgets/investments/_sidebar.html.erb index 866d4a9e7..4f18054f4 100644 --- a/app/views/budgets/investments/_sidebar.html.erb +++ b/app/views/budgets/investments/_sidebar.html.erb @@ -71,4 +71,8 @@ <% end %> <% end %> + + <%= link_to t("budgets.investments.header.check_ballot"), + budget_ballot_path(@budget), + class: "button hollow expanded" %> <% end %> diff --git a/app/views/layouts/_officing_booth.html.erb b/app/views/layouts/_officing_booth.html.erb new file mode 100644 index 000000000..bee7e78d7 --- /dev/null +++ b/app/views/layouts/_officing_booth.html.erb @@ -0,0 +1,5 @@ +<% if current_user.poll_officer? %> +
+ <%= t("admin.officing_booth.title", booth: try(:current_booth).try(:location)) %> +
+<% end %> diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb index 4b772f614..6b231ce49 100644 --- a/app/views/layouts/admin.html.erb +++ b/app/views/layouts/admin.html.erb @@ -33,6 +33,7 @@
<%= render "layouts/flash" %> + <%= render "layouts/officing_booth" %> <%= yield %>
diff --git a/app/views/officing/booth/new.html.erb b/app/views/officing/booth/new.html.erb new file mode 100644 index 000000000..6cbfeda0f --- /dev/null +++ b/app/views/officing/booth/new.html.erb @@ -0,0 +1,25 @@ +
+
+
+

+ <%= t("officing.booth.new.title") %> +

+ <%= form_for Poll::Booth.new, + as: :booth, + url: officing_booth_path, + method: :post do |f| %> +
+
+ <%= f.select :id, + @booths.collect { |booth| [booth.location, booth.id] }, + selected: @booths.first, + label: false, + tabindex: "1" %> + + <%= f.submit(t("devise_views.sessions.new.submit"), class: "button expanded") %> +
+
+ <% end %> +
+
+
diff --git a/app/views/officing/dashboard/index.html.erb b/app/views/officing/dashboard/index.html.erb index 87ee59926..792786434 100644 --- a/app/views/officing/dashboard/index.html.erb +++ b/app/views/officing/dashboard/index.html.erb @@ -3,7 +3,7 @@

<%= t("officing.dashboard.index.info") %>

- <% unless final_recount_shift? && vote_collection_shift? %> + <% if no_shifts? %>
<%= t("officing.dashboard.index.no_shifts") %>
diff --git a/app/views/officing/residence/new.html.erb b/app/views/officing/residence/new.html.erb index a508fae52..c736b1f23 100644 --- a/app/views/officing/residence/new.html.erb +++ b/app/views/officing/residence/new.html.erb @@ -1,32 +1,25 @@

<%= t("officing.residence.new.title") %>

-<% if @officer_assignments.present? %> -
-
- <%= form_for @residence, as: "residence", url: officing_residence_path do |f| %> - <%= render "errors" %> + -<% else %> -
- <%= t("officing.residence.new.no_assignments") %> -
-<% end %> +
diff --git a/app/views/officing/voters/create.js.erb b/app/views/officing/voters/create.js.erb index 676d153bd..eebdb9100 100644 --- a/app/views/officing/voters/create.js.erb +++ b/app/views/officing/voters/create.js.erb @@ -1,3 +1,4 @@ $("#<%= dom_id(@poll) %> #actions").html("<%= j render("voted") %>"); $("#<%= dom_id(@poll) %> #can_vote_callout").hide(); +$("#not_voted").hide(); $(".js-vote-collection").removeClass("is-hidden"); diff --git a/app/views/officing/voters/new.html.erb b/app/views/officing/voters/new.html.erb index 474b3c2a6..a61f4f842 100644 --- a/app/views/officing/voters/new.html.erb +++ b/app/views/officing/voters/new.html.erb @@ -28,4 +28,8 @@ <% end %> -<%= link_to t("officing.voters.new.not_to_vote"), namespaced_root_path, class: "button" %> +<% if Poll.votable_by(@user).any? %> +
+ <%= link_to t("officing.voters.new.not_to_vote"), namespaced_root_path, class: "button" %> +
+<% end %> diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 857e561de..c4680c3cb 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -14,6 +14,8 @@ en: edit: Edit configure: Configure delete: Delete + officing_booth: + title: "You are officing the booth located at %{booth}. If this is not correct, do not continue and call the help phone number. Thank you." banners: index: title: Banners @@ -191,6 +193,7 @@ en: undecided: Undecided unfeasible: Unfeasible min_total_supports: Minimum supports + max_total_supports: Maximum supports winners: Winners one_filter_html: "Current applied filters: %{filter}" two_filters_html: "Current applied filters: %{filter}, %{advanced_filters}" @@ -204,7 +207,7 @@ en: no_valuators_assigned: No valuators assigned no_valuation_groups: No valuation groups assigned feasibility: - feasible: "Feasible (%{price})" + feasible: "Feasible" unfeasible: "Unfeasible" undecided: "Undecided" selected: "Selected" @@ -223,6 +226,7 @@ en: visible_to_valuators: Show to valuators author_username: Author username incompatible: Incompatible + price: Price cannot_calculate_winners: The budget has to stay on phase "Balloting projects", "Reviewing Ballots" or "Finished budget" in order to calculate winners projects see_results: "See results" show: diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index 107b6d747..3fd88cd48 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -10,7 +10,8 @@ en: voted_html: one: "You have voted one investment." other: "You have voted %{count} investments." - voted_info_html: "You can change your vote at any time until the close of this phase.
No need to spend all the money available." + voted_info_html: "Your ballot is confirmed!" + voted_info_2: "But you can change your vote at any time until this phase is closed." zero: You have not voted any investment project. reasons_for_not_balloting: not_logged_in: You must %{signin} or %{signup} to continue. @@ -90,7 +91,7 @@ en: voted_info_link: change your vote different_heading_assigned_html: "You have active votes in another heading: %{heading_link}" change_ballot: "If your change your mind you can remove your votes in %{check_ballot} and start again." - check_ballot_link: "check my ballot" + check_ballot_link: "check and confirm my ballot" zero: You have not voted any investment project in this group. verified_only: "To create a new budget investment %{verify}." verify_account: "verify your account" @@ -142,10 +143,10 @@ en: zero: No supports give_support: Support header: - check_ballot: Check my ballot + check_ballot: Check and confirm my ballot different_heading_assigned_html: "You have active votes in another heading: %{heading_link}" change_ballot: "If your change your mind you can remove your votes in %{check_ballot} and start again." - check_ballot_link: "check my ballot" + check_ballot_link: "check and confirm my ballot" price: "This heading has a budget of" progress_bar: assigned: "You have assigned: " diff --git a/config/locales/en/officing.yml b/config/locales/en/officing.yml index 3eb0e70d7..e377654f0 100644 --- a/config/locales/en/officing.yml +++ b/config/locales/en/officing.yml @@ -16,6 +16,9 @@ en: no_polls: You are not officing final recounts in any active poll select_poll: Select poll add_results: Add results + booth: + new: + title: "Choose your booth" results: flash: create: "Results saved" @@ -51,7 +54,6 @@ en: submit: Validate document error_verifying_census: "The Census was unable to verify this document." form_errors: prevented the verification of this document - no_assignments: "You don't have officing shifts today" voters: new: title: Polls diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 0cd5d194d..bd192caaa 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -14,6 +14,8 @@ es: edit: Editar configure: Configurar delete: Borrar + officing_booth: + title: "Estás ahora mismo en la mesa ubicada en %{booth}. Si esto no es correcto no sigas adelante y llama al teléfono de incidencias. Gracias." banners: index: title: Banners @@ -191,6 +193,7 @@ es: undecided: Sin decidir unfeasible: Inviables min_total_supports: Apoyos mínimos + max_total_supports: Apoyos máximos winners: Ganadores one_filter_html: "Filtros en uso: %{filter}" two_filters_html: "Filtros en uso: %{filter}, %{advanced_filters}" @@ -204,7 +207,7 @@ es: no_valuators_assigned: Sin evaluador no_valuation_groups: Sin grupos evaluadores feasibility: - feasible: "Viable (%{price})" + feasible: "Viable" unfeasible: "Inviable" undecided: "Sin decidir" selected: "Seleccionado" @@ -223,6 +226,7 @@ es: visible_to_valuators: Mostrar a evaluadores author_username: Usuario autor incompatible: Incompatible + price: Precio cannot_calculate_winners: El presupuesto debe estar en las fases "Votación final", "Votación finalizada" o "Resultados" para poder calcular las propuestas ganadoras see_results: "Ver resultados" show: diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index 123b77f21..c827b2349 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -10,7 +10,8 @@ es: voted_html: one: "Has votado un proyecto." other: "Has votado %{count} proyectos." - voted_info_html: "Puedes cambiar tus votos en cualquier momento hasta el cierre de esta fase.
No hace falta que gastes todo el dinero disponible." + voted_info_html: "¡Tus votos están confirmados!" + voted_info_2: "Pero puedes cambiarlos en cualquier momento hasta el cierre de esta fase." zero: Todavía no has votado ningún proyecto de gasto. reasons_for_not_balloting: not_logged_in: Necesitas %{signin} o %{signup} para continuar. @@ -90,7 +91,7 @@ es: voted_info_link: cambiar tus votos different_heading_assigned_html: "Ya apoyaste proyectos de otra sección del presupuesto: %{heading_link}" change_ballot: "Si cambias de opinión puedes borrar tus votos en %{check_ballot} y volver a empezar." - check_ballot_link: "revisar mis votos" + check_ballot_link: "revisar y confirmar mis votos" zero: Todavía no has votado ningún proyecto de gasto en este ámbito del presupuesto. verified_only: "Para crear un nuevo proyecto de gasto %{verify}." verify_account: "verifica tu cuenta" @@ -142,10 +143,10 @@ es: other: "%{count} apoyos" give_support: Apoyar header: - check_ballot: Revisar mis votos + check_ballot: Revisar y confirmar mis votos different_heading_assigned_html: "Ya apoyaste proyectos de otra sección del presupuesto: %{heading_link}" change_ballot: "Si cambias de opinión puedes borrar tus votos en %{check_ballot} y volver a empezar." - check_ballot_link: "revisar mis votos" + check_ballot_link: "revisar y confirmar mis votos" price: "Esta partida tiene un presupuesto de" progress_bar: assigned: "Has asignado: " diff --git a/config/locales/es/officing.yml b/config/locales/es/officing.yml index c1d1fa837..e25f9cde8 100644 --- a/config/locales/es/officing.yml +++ b/config/locales/es/officing.yml @@ -16,6 +16,9 @@ es: no_polls: No tienes permiso para recuento final en ninguna votación reciente select_poll: Selecciona votación add_results: Añadir resultados + booth: + new: + title: "Escoge tu urna" results: flash: create: "Datos guardados" @@ -51,7 +54,6 @@ es: submit: Validar documento error_verifying_census: "El Padrón no pudo verificar este documento." form_errors: evitaron verificar este documento - no_assignments: "Hoy no tienes turno de presidente de mesa" voters: new: title: Votaciones diff --git a/config/routes/officing.rb b/config/routes/officing.rb index 7adfbbf60..6be5e5bc2 100644 --- a/config/routes/officing.rb +++ b/config/routes/officing.rb @@ -4,6 +4,7 @@ namespace :officing do resources :results, only: [:new, :create, :index] end + resource :booth, controller: "booth", only: [:new, :create] resource :residence, controller: "residence", only: [:new, :create] resources :voters, only: [:new, :create] root to: "dashboard#index" diff --git a/db/dev_seeds/polls.rb b/db/dev_seeds/polls.rb index 8712ce827..8fa43823c 100644 --- a/db/dev_seeds/polls.rb +++ b/db/dev_seeds/polls.rb @@ -42,13 +42,13 @@ end section "Creating Poll Questions & Answers" do Poll.find_each do |poll| (1..4).to_a.sample.times do - title = Faker::Lorem.sentence(3).truncate(60) + "?" + question_title = Faker::Lorem.sentence(3).truncate(60) + "?" question = Poll::Question.new(author: User.all.sample, - title: title, + title: question_title, poll: poll) I18n.available_locales.map do |locale| Globalize.with_locale(locale) do - question.title = "#{title} (#{locale})" + question.title = "#{question_title} (#{locale})" end end question.save! @@ -115,12 +115,16 @@ end section "Creating Poll Voters" do def vote_poll_on_booth(user, poll) + officer = Poll::Officer.all.sample + Poll::Voter.create!(document_type: user.document_type, document_number: user.document_number, user: user, poll: poll, origin: "booth", - officer: Poll::Officer.all.sample) + officer: officer, + officer_assignment: officer.officer_assignments.sample, + booth_assignment: poll.booth_assignments.sample) end def vote_poll_on_web(user, poll) @@ -143,11 +147,11 @@ section "Creating Poll Voters" do end (Poll.expired + Poll.current + Poll.recounting).uniq.each do |poll| - level_two_verified_users = User.level_two_verified + verified_users = User.level_two_or_three_verified if poll.geozone_restricted? - level_two_verified_users = level_two_verified_users.where(geozone_id: poll.geozone_ids) + verified_users = verified_users.where(geozone_id: poll.geozone_ids) end - user_groups = level_two_verified_users.in_groups(2) + user_groups = verified_users.in_groups(2) user_groups.first.each { |user| vote_poll_on_booth(user, poll) } user_groups.second.compact.each { |user| vote_poll_on_web(user, poll) } end @@ -159,13 +163,23 @@ section "Creating Poll Recounts" do officer_assignment = poll.officer_assignments.first author = Poll::Officer.first.user + total_amount = white_amount = null_amount = 0 + + booth_assignment.voters.count.times do + case rand + when 0...0.1 then null_amount += 1 + when 0.1...0.2 then white_amount += 1 + else total_amount += 1 + end + end + Poll::Recount.create!(officer_assignment: officer_assignment, booth_assignment: booth_assignment, author: author, date: poll.ends_at, - white_amount: rand(0..10), - null_amount: rand(0..10), - total_amount: rand(100..9999), + white_amount: white_amount, + null_amount: null_amount, + total_amount: total_amount, origin: "booth") end end diff --git a/db/dev_seeds/users.rb b/db/dev_seeds/users.rb index 199b16dfb..95330b645 100644 --- a/db/dev_seeds/users.rb +++ b/db/dev_seeds/users.rb @@ -8,7 +8,7 @@ section "Creating Users" do password_confirmation: password, confirmed_at: Time.current, terms_of_service: "1", - gender: ["Male", "Female"].sample, + gender: %w[male female].sample, date_of_birth: rand((Time.current - 80.years)..(Time.current - 16.years)), public_activity: (rand(1..100) > 30) ) diff --git a/spec/factories/budgets.rb b/spec/factories/budgets.rb index 0b9130825..b53bd8b56 100644 --- a/spec/factories/budgets.rb +++ b/spec/factories/budgets.rb @@ -150,13 +150,17 @@ FactoryBot.define do valuation_finished true end - trait :hidden do - hidden_at { Time.current } - end + trait :hidden do + hidden_at { Time.current } + end - trait :with_ignored_flag do - ignored_flag_at { Time.current } - end + trait :with_ignored_flag do + ignored_flag_at { Time.current } + end + + trait :with_administrator do + administrator + end trait :flagged do after :create do |investment| diff --git a/spec/features/admin/budget_investments_spec.rb b/spec/features/admin/budget_investments_spec.rb index ea3711364..bfc8e7881 100644 --- a/spec/features/admin/budget_investments_spec.rb +++ b/spec/features/admin/budget_investments_spec.rb @@ -251,12 +251,161 @@ feature "Admin budget investments" do expect(page).not_to have_link("Build a hospital") end + scenario "Filtering by without assigned admin", :js do + create(:budget_investment, + title: "Investment without admin", + budget: budget) + create(:budget_investment, + :with_administrator, + title: "Investment with admin", + budget: budget) + + visit admin_budget_budget_investments_path(budget_id: budget) + expect(page).to have_link("Investment without admin") + expect(page).to have_link("Investment with admin") + + click_link "Advanced filters" + check("Without assigned admin") + click_button "Filter" + + expect(page).to have_content("There is 1 investment") + expect(page).to have_link("Investment without admin") + expect(page).not_to have_link("Investment with admin") + + uncheck("Without assigned admin") + click_button "Filter" + + expect(page).to have_content("There are 2 investments") + expect(page).to have_link("Investment without admin") + expect(page).to have_link("Investment with admin") + end + + scenario "Filtering by without assigned valuator", :js do + user = create(:user) + valuator = create(:valuator, user: user) + create(:budget_investment, + title: "Investment without valuator", + budget: budget) + create(:budget_investment, + title: "Investment with valuator", + budget: budget, + valuators: [valuator]) + + visit admin_budget_budget_investments_path(budget_id: budget) + expect(page).to have_link("Investment without valuator") + expect(page).to have_link("Investment with valuator") + + click_link "Advanced filters" + check "Without assigned valuator" + click_button "Filter" + + expect(page).to have_content("There is 1 investment") + expect(page).to have_link("Investment without valuator") + expect(page).not_to have_link("Investment with valuator") + + uncheck "Without assigned valuator" + click_button "Filter" + + expect(page).to have_content("There are 2 investments") + expect(page).to have_link("Investment without valuator") + expect(page).to have_link("Investment with valuator") + end + + scenario "Filtering by under valuation", :js do + user = create(:user) + valuator = create(:valuator, user: user) + create(:budget_investment, + :with_administrator, + valuation_finished: false, + title: "Investment without valuation", + budget: budget, + valuators: [valuator]) + create(:budget_investment, + :with_administrator, + title: "Investment with valuation", + budget: budget) + + visit admin_budget_budget_investments_path(budget_id: budget) + expect(page).to have_link("Investment without valuation") + expect(page).to have_link("Investment with valuation") + + click_link "Advanced filters" + check "Under valuation" + click_button "Filter" + + expect(page).to have_content("There is 1 investment") + expect(page).to have_link("Investment without valuation") + expect(page).not_to have_link("Investment with valuation") + + uncheck "Under valuation" + click_button "Filter" + + expect(page).to have_content("There are 2 investments") + expect(page).to have_link("Investment without valuation") + expect(page).to have_link("Investment with valuation") + end + + scenario "Filtering by valuation finished", :js do + create(:budget_investment, + title: "Investment valuation open", + budget: budget) + create(:budget_investment, + :finished, + title: "Investment valuation finished", + budget: budget) + + visit admin_budget_budget_investments_path(budget_id: budget) + expect(page).to have_link("Investment valuation open") + expect(page).to have_link("Investment valuation finished") + + click_link "Advanced filters" + check "Valuation finished" + click_button "Filter" + + expect(page).to have_content("There is 1 investment") + expect(page).not_to have_link("Investment valuation open") + expect(page).to have_link("Investment valuation finished") + + uncheck "Valuation finished" + click_button "Filter" + + expect(page).to have_content("There are 2 investments") + expect(page).to have_link("Investment valuation open") + expect(page).to have_link("Investment valuation finished") + end + + scenario "Filtering by winners", :js do + create(:budget_investment, + :winner, + valuation_finished: true, + title: "Investment winner", + budget: budget) + create(:budget_investment, + title: "Investment without winner", + budget: budget) + + visit admin_budget_budget_investments_path(budget_id: budget) + expect(page).to have_link("Investment winner") + expect(page).to have_link("Investment without winner") + + click_link "Advanced filters" + check "Winners" + click_button "Filter" + + expect(page).to have_content("There is 1 investment") + expect(page).to have_link("Investment winner") + expect(page).not_to have_link("Investment without winner") + + uncheck "Winners" + click_button "Filter" + + expect(page).to have_content("There are 2 investments") + expect(page).to have_link("Investment winner") + expect(page).to have_link("Investment without winner") + end + scenario "Current filter is properly highlighted" do - filters_links = { "all" => "All", - "without_admin" => "Without assigned admin", - "without_valuator" => "Without assigned valuator", - "under_valuation" => "Under valuation", - "valuation_finished" => "Valuation finished" } + filters_links = { "all" => "All" } visit admin_budget_budget_investments_path(budget_id: budget.id) @@ -288,13 +437,15 @@ feature "Admin budget investments" do expect(page).to have_content("Evaluating...") expect(page).to have_content("With group") - visit admin_budget_budget_investments_path(budget_id: budget.id, filter: "without_admin") + visit admin_budget_budget_investments_path(budget_id: budget.id, + advanced_filters: ["without_admin"]) expect(page).to have_content("Evaluating...") expect(page).to have_content("With group") expect(page).not_to have_content("Assigned idea") - visit admin_budget_budget_investments_path(budget_id: budget.id, filter: "without_valuator") + visit admin_budget_budget_investments_path(budget_id: budget.id, + advanced_filters: ["without_valuator"]) expect(page).to have_content("Assigned idea") expect(page).not_to have_content("Evaluating...") @@ -309,17 +460,20 @@ feature "Admin budget investments" do valuating.valuators.push(create(:valuator)) valuated.valuators.push(create(:valuator)) - visit admin_budget_budget_investments_path(budget_id: budget.id, filter: "under_valuation") + query_params = {budget_id: budget.id, advanced_filters: ["under_valuation"]} + + visit admin_budget_budget_investments_path(query_params) expect(page).to have_content("Ongoing valuation") expect(page).not_to have_content("Old idea") - visit admin_budget_budget_investments_path(budget_id: budget.id, filter: "valuation_finished") + visit admin_budget_budget_investments_path(budget_id: budget.id, + advanced_filters: ["valuation_finished"]) expect(page).not_to have_content("Ongoing valuation") expect(page).to have_content("Old idea") - visit admin_budget_budget_investments_path(budget_id: budget.id, filter: "all") + visit admin_budget_budget_investments_path(budget_id: budget.id, advanced_filters: ["filter"]) expect(page).to have_content("Ongoing valuation") expect(page).to have_content("Old idea") end @@ -380,7 +534,10 @@ feature "Admin budget investments" do budget.update(phase: "reviewing_ballots") visit admin_budget_budget_investments_path(budget) - click_link "Winners" + + click_link "Advanced filters" + check "Winners" + click_button "Filter" expect(page).to have_link "Calculate Winner Investments" @@ -391,7 +548,9 @@ feature "Admin budget investments" do budget.update(phase: "accepting") visit admin_budget_budget_investments_path(budget) - click_link "Winners" + + check "Winners" + click_button "Filter" expect(page).not_to have_link "Calculate Winner Investments" expect(page).to have_content 'The budget has to stay on phase "Balloting projects", '\ @@ -439,6 +598,42 @@ feature "Admin budget investments" do expect(page).not_to have_link("Road 100 supports") end + scenario "Filtering by maximum number of votes", :js do + group_1 = create(:budget_group, budget: budget) + group_2 = create(:budget_group, budget: budget) + parks = create(:budget_heading, group: group_1) + roads = create(:budget_heading, group: group_2) + streets = create(:budget_heading, group: group_2) + + create(:budget_investment, heading: parks, cached_votes_up: 40, title: "Park 40 supports") + create(:budget_investment, heading: parks, cached_votes_up: 99, title: "Park 99 supports") + create(:budget_investment, heading: roads, cached_votes_up: 100, title: "Road 100 supports") + create(:budget_investment, heading: roads, cached_votes_up: 199, title: "Road 199 supports") + create(:budget_investment, heading: streets, cached_votes_up: 200, title: "St. 200 supports") + create(:budget_investment, heading: streets, cached_votes_up: 300, title: "St. 300 supports") + + visit admin_budget_budget_investments_path(budget) + + expect(page).to have_link("Park 40 supports") + expect(page).to have_link("Park 99 supports") + expect(page).to have_link("Road 100 supports") + expect(page).to have_link("Road 199 supports") + expect(page).to have_link("St. 200 supports") + expect(page).to have_link("St. 300 supports") + + click_link "Advanced filters" + fill_in "max_total_supports", with: 180 + click_button "Filter" + + expect(page).to have_content("There are 3 investments") + expect(page).not_to have_link("Road 199 supports") + expect(page).not_to have_link("St. 200 supports") + expect(page).not_to have_link("St. 300 supports") + expect(page).to have_link("Park 40 supports") + expect(page).to have_link("Park 99 supports") + expect(page).to have_link("Road 100 supports") + end + scenario "Combination of checkbox with text search", :js do user = create(:user, username: "Admin 1") administrator = create(:administrator, user: user) @@ -494,7 +689,7 @@ feature "Admin budget investments" do click_link "Advanced filters" - page.check("advanced_filters_feasible") + check("Feasible") click_button "Filter" expect(page).to have_css(".budget_investment", count: 2) @@ -549,7 +744,7 @@ feature "Admin budget investments" do click_link "Advanced filters" - within("#advanced_filters") { check("advanced_filters_feasible") } + within("#advanced_filters") { check("Feasible") } click_button("Filter") expect(page).to have_css(".budget_investment", count: 2) @@ -1120,16 +1315,17 @@ feature "Admin budget investments" do scenario "Filtering by valuation and selection", :js do visit admin_budget_budget_investments_path(budget) - within("#filter-subnav") { click_link "Valuation finished" } + click_link "Advanced filters" + check "Valuation finished" + click_button "Filter" + expect(page).not_to have_content(unfeasible_bi.title) expect(page).not_to have_content(feasible_bi.title) expect(page).to have_content(feasible_vf_bi.title) expect(page).to have_content(selected_bi.title) expect(page).to have_content(winner_bi.title) - click_link "Advanced filters" - - within("#advanced_filters") { check("advanced_filters_feasible") } + within("#advanced_filters") { check("Feasible") } click_button("Filter") expect(page).not_to have_content(unfeasible_bi.title) @@ -1139,8 +1335,8 @@ feature "Admin budget investments" do expect(page).to have_content(winner_bi.title) within("#advanced_filters") do - check("advanced_filters_selected") - uncheck("advanced_filters_feasible") + check("Selected") + uncheck("Feasible") end click_button("Filter") @@ -1151,7 +1347,9 @@ feature "Admin budget investments" do expect(page).to have_content(selected_bi.title) expect(page).to have_content(winner_bi.title) - within("#filter-subnav") { click_link "Winners" } + check "Winners" + click_button "Filter" + expect(page).not_to have_content(unfeasible_bi.title) expect(page).not_to have_content(feasible_bi.title) expect(page).not_to have_content(feasible_vf_bi.title) @@ -1164,7 +1362,7 @@ feature "Admin budget investments" do click_link "Advanced filters" - within("#advanced_filters") { check("advanced_filters_undecided") } + within("#advanced_filters") { check("Undecided") } click_button("Filter") expect(page).to have_content(undecided_bi.title) @@ -1174,7 +1372,7 @@ feature "Admin budget investments" do expect(page).not_to have_content(unfeasible_bi.title) expect(page).not_to have_content(feasible_vf_bi.title) - within("#advanced_filters") { check("advanced_filters_unfeasible") } + within("#advanced_filters") { check("Unfeasible") } click_button("Filter") expect(page).to have_content(undecided_bi.title) @@ -1219,7 +1417,7 @@ feature "Admin budget investments" do click_link "Advanced filters" - within("#advanced_filters") { check("advanced_filters_selected") } + within("#advanced_filters") { check("Selected") } click_button("Filter") within("#budget_investment_#{feasible_vf_bi.id}") do @@ -1232,7 +1430,7 @@ feature "Admin budget investments" do visit admin_budget_budget_investments_path(budget) click_link "Advanced filters" - within("#advanced_filters") { check("advanced_filters_selected") } + within("#advanced_filters") { check("Selected") } click_button("Filter") expect(page).to have_content("There are 2 investments") @@ -1289,15 +1487,18 @@ feature "Admin budget investments" do investment2.update(administrator: admin) visit admin_budget_budget_investments_path(budget) - within("#filter-subnav") { click_link "Under valuation" } - expect(page).not_to have_link("Under valuation") + click_link "Advanced filters" + check "Under valuation" + click_button "Filter" within("#budget_investment_#{investment1.id}") do check "budget_investment_visible_to_valuators" end visit admin_budget_budget_investments_path(budget) - within("#filter-subnav") { click_link "Under valuation" } + click_link "Advanced filters" + check "Under valuation" + click_button "Filter" within("#budget_investment_#{investment1.id}") do expect(find("#budget_investment_visible_to_valuators")).to be_checked @@ -1335,15 +1536,20 @@ feature "Admin budget investments" do investment2.update(administrator: admin, visible_to_valuators: true) visit admin_budget_budget_investments_path(budget) - within("#filter-subnav") { click_link "Under valuation" } - expect(page).not_to have_link("Under valuation") + + click_link "Advanced filters" + check "Under valuation" + click_button "Filter" within("#budget_investment_#{investment1.id}") do uncheck "budget_investment_visible_to_valuators" end visit admin_budget_budget_investments_path(budget) - within("#filter-subnav") { click_link "Under valuation" } + + click_link "Advanced filters" + check "Under valuation" + click_button "Filter" within("#budget_investment_#{investment1.id}") do expect(find("#budget_investment_visible_to_valuators")).not_to be_checked @@ -1364,7 +1570,9 @@ feature "Admin budget investments" do expect(page).to have_css("#budget_investment_visible_to_valuators") - within("#filter-subnav") { click_link "Under valuation" } + click_link "Advanced filters" + check "Under valuation" + click_button "Filter" within("#budget_investment_#{investment1.id}") do valuating_checkbox = find("#budget_investment_visible_to_valuators") @@ -1442,7 +1650,9 @@ feature "Admin budget investments" do create(:budget_investment, :finished, budget: budget, title: "Finished Investment") visit admin_budget_budget_investments_path(budget) - within("#filter-subnav") { click_link "Valuation finished" } + click_link "Advanced filters" + check "Valuation finished" + click_button "Filter" click_link "Download current selection" diff --git a/spec/features/admin/budgets_spec.rb b/spec/features/admin/budgets_spec.rb index e24c344b2..38347b56e 100644 --- a/spec/features/admin/budgets_spec.rb +++ b/spec/features/admin/budgets_spec.rb @@ -281,7 +281,9 @@ feature "Admin budgets" do expect(page).not_to have_content "Calculate Winner Investments" visit admin_budget_budget_investments_path(budget) - click_link "Winners" + click_link "Advanced filters" + check "Winners" + click_button "Filter" expect(page).to have_content "Recalculate Winner Investments" expect(page).not_to have_content "Calculate Winner Investments" diff --git a/spec/features/budget_polls/officing_spec.rb b/spec/features/budget_polls/officing_spec.rb index 7f77b5caa..7b91eec12 100644 --- a/spec/features/budget_polls/officing_spec.rb +++ b/spec/features/budget_polls/officing_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" feature "Budget Poll Officing" do - scenario "Show sidebar menu if officer has shifts assigned" do + scenario "Show sidebar menus if officer has shifts assigned" do poll = create(:poll) booth = create(:poll_booth) booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth) @@ -10,13 +10,21 @@ feature "Budget Poll Officing" do user = create(:user) officer = create(:poll_officer, user: user) + create(:poll_shift, officer: officer, booth: booth, date: Date.current, task: :vote_collection) + + login_as user + visit officing_root_path + + expect(page).not_to have_content("You don't have officing shifts today") + expect(page).to have_content("Validate document") + expect(page).not_to have_content("Total recounts and results") + create(:poll_shift, officer: officer, booth: booth, date: Date.current, task: :recount_scrutiny) officer_assignment = create(:poll_officer_assignment, booth_assignment: booth_assignment, officer: officer) - login_as user visit officing_root_path expect(page).not_to have_content("You don't have officing shifts today") @@ -24,7 +32,7 @@ feature "Budget Poll Officing" do expect(page).to have_content("Total recounts and results") end - scenario "Do not show sidebar menu if officer has no shifts assigned" do + scenario "Do not show sidebar menus if officer has no shifts assigned" do user = create(:user) officer = create(:poll_officer, user: user) diff --git a/spec/features/budgets/ballots_spec.rb b/spec/features/budgets/ballots_spec.rb index 4398ab07f..639c4e75a 100644 --- a/spec/features/budgets/ballots_spec.rb +++ b/spec/features/budgets/ballots_spec.rb @@ -117,6 +117,7 @@ feature "Ballots" do within("#sidebar") do expect(page).to have_content investment1.title expect(page).to have_content "€10,000" + expect(page).to have_link("Check and confirm my ballot") end add_to_ballot(investment2) @@ -127,6 +128,7 @@ feature "Ballots" do within("#sidebar") do expect(page).to have_content investment2.title expect(page).to have_content "€20,000" + expect(page).to have_link("Check and confirm my ballot") end end @@ -146,6 +148,7 @@ feature "Ballots" do within("#sidebar") do expect(page).to have_content investment.title expect(page).to have_content "€10,000" + expect(page).to have_link("Check and confirm my ballot") end within("#budget_investment_#{investment.id}") do @@ -158,6 +161,7 @@ feature "Ballots" do within("#sidebar") do expect(page).not_to have_content investment.title expect(page).not_to have_content "€10,000" + expect(page).to have_link("Check and confirm my ballot") end end @@ -454,7 +458,9 @@ feature "Ballots" do visit budget_investments_path(budget, heading_id: new_york.id) add_to_ballot(investment) - click_link "Check my ballot" + within(".budget-heading") do + click_link "Check and confirm my ballot" + end expect(page).to have_content("You have voted one investment") diff --git a/spec/features/budgets/investments_spec.rb b/spec/features/budgets/investments_spec.rb index 26cb0350a..75967555a 100644 --- a/spec/features/budgets/investments_spec.rb +++ b/spec/features/budgets/investments_spec.rb @@ -583,43 +583,46 @@ feature "Budget Investments" do context "Orders" do before { budget.update(phase: "selecting") } + let(:per_page) { Budgets::InvestmentsController::PER_PAGE } scenario "Default order is random" do - per_page = Kaminari.config.default_per_page - (per_page + 100).times { create(:budget_investment) } - - visit budget_investments_path(budget, heading_id: heading.id) - order = all(".budget-investment h3").collect {|i| i.text } - - visit budget_investments_path(budget, heading_id: heading.id) - new_order = eq(all(".budget-investment h3").collect {|i| i.text }) - - expect(order).not_to eq(new_order) - end - - scenario "Random order after another order" do - per_page = Kaminari.config.default_per_page - (per_page + 2).times { create(:budget_investment) } - - visit budget_investments_path(budget, heading_id: heading.id) - click_link "highest rated" - click_link "random" - - order = all(".budget-investment h3").collect {|i| i.text } - - visit budget_investments_path(budget, heading_id: heading.id) - new_order = eq(all(".budget-investment h3").collect {|i| i.text }) - - expect(order).not_to eq(new_order) - end - - scenario "Random order maintained with pagination" do - per_page = Kaminari.config.default_per_page (per_page + 2).times { create(:budget_investment, heading: heading) } visit budget_investments_path(budget, heading_id: heading.id) - order = all(".budget-investment h3").collect {|i| i.text } + within(".submenu .is-active") { expect(page).to have_content "random" } + order = all(".budget-investment h3").collect { |i| i.text } + expect(order).not_to be_empty + + visit budget_investments_path(budget, heading_id: heading.id) + new_order = all(".budget-investment h3").collect { |i| i.text } + + expect(order).to eq(new_order) + end + + scenario "Random order after another order" do + (per_page + 2).times { create(:budget_investment, heading: heading) } + + visit budget_investments_path(budget, heading_id: heading.id) + order = all(".budget-investment h3").collect { |i| i.text } + expect(order).not_to be_empty + + click_link "highest rated" + click_link "random" + + visit budget_investments_path(budget, heading_id: heading.id) + new_order = all(".budget-investment h3").collect { |i| i.text } + + expect(order).to eq(new_order) + end + + scenario "Random order maintained with pagination" do + (per_page + 2).times { create(:budget_investment, heading: heading) } + + visit budget_investments_path(budget, heading_id: heading.id) + + order = all(".budget-investment h3").collect { |i| i.text } + expect(order).not_to be_empty click_link "Next" expect(page).to have_content "You're on page 2" @@ -627,27 +630,27 @@ feature "Budget Investments" do click_link "Previous" expect(page).to have_content "You're on page 1" - new_order = all(".budget-investment h3").collect {|i| i.text } + new_order = all(".budget-investment h3").collect { |i| i.text } expect(order).to eq(new_order) end scenario "Random order maintained when going back from show" do - 10.times { |i| create(:budget_investment, heading: heading) } + per_page.times { |i| create(:budget_investment, heading: heading) } visit budget_investments_path(budget, heading_id: heading.id) - order = all(".budget-investment h3").collect {|i| i.text } + order = all(".budget-investment h3").collect { |i| i.text } + expect(order).not_to be_empty click_link Budget::Investment.first.title click_link "Go back" - new_order = all(".budget-investment h3").collect {|i| i.text } + new_order = all(".budget-investment h3").collect { |i| i.text } expect(order).to eq(new_order) end scenario "Investments are not repeated with random order" do - 12.times { create(:budget_investment, heading: heading) } - # 12 instead of per_page + 2 because in each page there are 10 (in this case), not 25 + (per_page + 2).times { create(:budget_investment, heading: heading) } visit budget_investments_path(budget, order: "random") @@ -661,7 +664,6 @@ feature "Budget Investments" do common_values = first_page_investments & second_page_investments expect(common_values.length).to eq(0) - end scenario "Proposals are ordered by confidence_score" do @@ -686,7 +688,7 @@ feature "Budget Investments" do end scenario "Each user has a different and consistent random budget investment order" do - (Kaminari.config.default_per_page * 1.3).to_i.times { create(:budget_investment, heading: heading) } + (per_page * 1.3).to_i.times { create(:budget_investment, heading: heading) } in_browser(:one) do visit budget_investments_path(budget, heading: heading) @@ -722,7 +724,7 @@ feature "Budget Investments" do end scenario "Each user has a equal and consistent budget investment order when the random_seed is equal" do - (Kaminari.config.default_per_page * 1.3).to_i.times { create(:budget_investment, heading: heading) } + (per_page * 1.3).to_i.times { create(:budget_investment, heading: heading) } in_browser(:one) do visit budget_investments_path(budget, heading: heading, random_seed: "1") @@ -741,10 +743,10 @@ feature "Budget Investments" do voter = create(:user, :level_two) login_as(voter) - 10.times { create(:budget_investment, heading: heading) } + per_page.times { create(:budget_investment, heading: heading) } voted_investments = [] - 10.times do + per_page.times do investment = create(:budget_investment, heading: heading) create(:vote, votable: investment, voter: voter) voted_investments << investment @@ -762,17 +764,18 @@ feature "Budget Investments" do end scenario "Order is random if budget is finished" do - 10.times { create(:budget_investment) } + per_page.times { create(:budget_investment, :winner, heading: heading) } budget.update(phase: "finished") visit budget_investments_path(budget, heading_id: heading.id) - order = all(".budget-investment h3").collect {|i| i.text } + order = all(".budget-investment h3").collect { |i| i.text } + expect(order).not_to be_empty visit budget_investments_path(budget, heading_id: heading.id) - new_order = eq(all(".budget-investment h3").collect {|i| i.text }) + new_order = all(".budget-investment h3").collect { |i| i.text } - expect(order).not_to eq(new_order) + expect(order).to eq(new_order) end scenario "Order always is random for unfeasible and unselected investments" do @@ -930,10 +933,12 @@ feature "Budget Investments" do visit budget_investments_path(budget, heading_id: heading.id) - expect(page).not_to have_link("Check my ballot") + expect(page).not_to have_link("Check and confirm my ballot") expect(page).not_to have_css("#progress_bar") + within("#sidebar") do expect(page).not_to have_content("My ballot") + expect(page).not_to have_link("Check and confirm my ballot") end end @@ -1564,7 +1569,8 @@ feature "Budget Investments" do visit budget_ballot_path(budget) - expect(page).to have_content "You can change your vote at any time until the close of this phase" + expect(page).to have_content "But you can change your vote at any time "\ + "until this phase is closed." within("#budget_group_#{global_group.id}") do expect(page).to have_content sp1.title @@ -1627,10 +1633,12 @@ feature "Budget Investments" do visit budget_investments_path(budget, heading_id: heading.id) - expect(page).to have_link("Check my ballot") + expect(page).to have_link("Check and confirm my ballot") expect(page).to have_css("#progress_bar") + within("#sidebar") do expect(page).to have_content("My ballot") + expect(page).to have_link("Check and confirm my ballot") end end diff --git a/spec/features/officing/booth_spec.rb b/spec/features/officing/booth_spec.rb new file mode 100644 index 000000000..72eddbdbc --- /dev/null +++ b/spec/features/officing/booth_spec.rb @@ -0,0 +1,87 @@ +require "rails_helper" + +feature "Booth", :with_frozen_time do + + scenario "Officer with no booth assignments today" do + officer = create(:poll_officer) + + login_through_form_as_officer(officer.user) + + expect(page).to have_content "You don't have officing shifts today" + end + + scenario "Officer with booth assignments another day" do + officer = create(:poll_officer) + create(:poll_officer_assignment, officer: officer, date: Date.current + 1.day) + + login_through_form_as_officer(officer.user) + + expect(page).to have_content "You don't have officing shifts today" + end + + scenario "Officer with single booth assignment today" do + officer = create(:poll_officer) + poll = create(:poll) + + booth = create(:poll_booth) + + booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth) + create(:poll_officer_assignment, officer: officer, booth_assignment: booth_assignment, date: Date.current) + + login_through_form_as_officer(officer.user) + + within("#officing-booth") do + expect(page).to have_content "You are officing the booth located at #{booth.location}." + end + end + + scenario "Officer with multiple booth assignments today" do + officer = create(:poll_officer) + poll = create(:poll) + + booth1 = create(:poll_booth) + booth2 = create(:poll_booth) + + ba1 = create(:poll_booth_assignment, poll: poll, booth: booth1) + ba2 = create(:poll_booth_assignment, poll: poll, booth: booth2) + + create(:poll_officer_assignment, officer: officer, booth_assignment: ba1, date: Date.current) + create(:poll_officer_assignment, officer: officer, booth_assignment: ba2, date: Date.current) + + login_through_form_as_officer(officer.user) + + expect(page).to have_content "Choose your booth" + + select booth2.location, from: "booth_id" + click_button "Enter" + + within("#officing-booth") do + expect(page).to have_content "You are officing the booth located at #{booth2.location}." + end + end + + scenario "Display single booth for any number of polls" do + officer = create(:poll_officer) + + booth1 = create(:poll_booth) + booth2 = create(:poll_booth) + + poll1 = create(:poll) + poll2 = create(:poll) + + ba1 = create(:poll_booth_assignment, poll: poll1, booth: booth1) + ba2 = create(:poll_booth_assignment, poll: poll2, booth: booth2) + ba3 = create(:poll_booth_assignment, poll: poll2, booth: booth2) + + create(:poll_officer_assignment, officer: officer, booth_assignment: ba1, date: Date.current) + create(:poll_officer_assignment, officer: officer, booth_assignment: ba2, date: Date.current) + create(:poll_officer_assignment, officer: officer, booth_assignment: ba3, date: Date.current) + + login_through_form_as_officer(officer.user) + + expect(page).to have_content "Choose your booth" + + expect(page).to have_select("booth_id", options: [booth1.location, booth2.location]) + end + +end diff --git a/spec/features/officing/residence_spec.rb b/spec/features/officing/residence_spec.rb index 9f07eb8ce..3908fa546 100644 --- a/spec/features/officing/residence_spec.rb +++ b/spec/features/officing/residence_spec.rb @@ -25,7 +25,7 @@ feature "Residence", :with_frozen_time do background do create(:poll_officer_assignment, officer: officer) - login_as(officer.user) + login_through_form_as_officer(officer.user) visit officing_root_path end @@ -93,4 +93,27 @@ feature "Residence", :with_frozen_time do end + scenario "Verify booth", :js do + booth = create(:poll_booth) + poll = create(:poll) + + ba = create(:poll_booth_assignment, poll: poll, booth: booth) + create(:poll_officer_assignment, officer: officer, booth_assignment: ba) + create(:poll_shift, officer: officer, booth: booth, date: Date.current) + + login_as(officer.user) + + visit new_officing_residence_path + within("#officing-booth") do + expect(page).to have_content "You are officing the booth located at #{booth.location}." + end + + officing_verify_residence + + expect(page).to have_content poll.name + click_button "Confirm vote" + + expect(page).to have_content "Vote introduced!" + end + end diff --git a/spec/features/officing/results_spec.rb b/spec/features/officing/results_spec.rb index 46e65c1a1..d4f9f3621 100644 --- a/spec/features/officing/results_spec.rb +++ b/spec/features/officing/results_spec.rb @@ -5,6 +5,7 @@ feature "Officing Results", :with_frozen_time do background do @poll_officer = create(:poll_officer) @officer_assignment = create(:poll_officer_assignment, :final, officer: @poll_officer) + create(:poll_shift, officer: @poll_officer, booth: @officer_assignment.booth, date: Date.current) @poll = @officer_assignment.booth_assignment.poll @poll.update(ends_at: 1.day.ago) @question_1 = create(:poll_question, poll: @poll) @@ -15,6 +16,7 @@ feature "Officing Results", :with_frozen_time do create(:poll_question_answer, title: "Tomorrow", question: @question_2, given_order: 2) login_as(@poll_officer.user) + set_officing_booth(@officer_assignment.booth) end scenario "Only polls where user is officer for results are accessible" do @@ -30,6 +32,7 @@ feature "Officing Results", :with_frozen_time do click_link "Polling officers" expect(page).to have_content("Poll officing") + within("#side_menu") do click_link "Total recounts and results" end diff --git a/spec/features/officing/voters_spec.rb b/spec/features/officing/voters_spec.rb index 067520d27..2f21234e7 100644 --- a/spec/features/officing/voters_spec.rb +++ b/spec/features/officing/voters_spec.rb @@ -12,9 +12,12 @@ feature "Voters" do create(:poll_shift, officer: officer, booth: booth, date: Date.current, task: :vote_collection) booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth) create(:poll_officer_assignment, officer: officer, booth_assignment: booth_assignment) + set_officing_booth(booth) end scenario "Can vote", :js do + create(:poll_officer_assignment, officer: officer) + visit new_officing_residence_path officing_verify_residence @@ -57,6 +60,10 @@ feature "Voters" do user = create(:user, residence_verified_at: Time.current, document_type: "1", document_number: "12345678Z") expect(user).not_to be_level_two_verified + visit root_path + click_link "Sign out" + login_through_form_as_officer(officer.user) + visit new_officing_residence_path officing_verify_residence @@ -83,6 +90,7 @@ feature "Voters" do booth_assignment = create(:poll_booth_assignment, poll: poll_geozone_restricted_out, booth: booth) create(:poll_officer_assignment, officer: officer, booth_assignment: booth_assignment) + set_officing_booth(second_booth) visit new_officing_residence_path officing_verify_residence @@ -93,4 +101,44 @@ feature "Voters" do expect(page).to have_content poll_geozone_restricted_in.name expect(page).not_to have_content poll_geozone_restricted_out.name end + + scenario "Store officer and booth information", :js do + create(:user, :in_census, id: rand(9999999)) + poll1 = create(:poll, name: "¿Quieres que XYZ sea aprobado?") + poll2 = create(:poll, name: "Pregunta de votación de prueba") + + second_booth = create(:poll_booth) + + ba1 = create(:poll_booth_assignment, poll: poll1, booth: second_booth ) + ba2 = create(:poll_booth_assignment, poll: poll2, booth: second_booth ) + create(:poll_shift, officer: officer, booth: second_booth, date: Date.current, task: :vote_collection) + + validate_officer + visit new_officing_residence_path + set_officing_booth(second_booth) + officing_verify_residence + + within("#poll_#{poll1.id}") do + click_button "Confirm vote" + + expect(page).to have_content "Vote introduced!" + end + + within("#poll_#{poll2.id}") do + click_button "Confirm vote" + + expect(page).to have_content "Vote introduced!" + end + + expect(Poll::Voter.count).to eq(2) + + voter1 = Poll::Voter.first + + expect(voter1.booth_assignment).to eq(ba1) + expect(voter1.officer_assignment).to eq(ba1.officer_assignments.first) + + voter2 = Poll::Voter.last + expect(voter2.booth_assignment).to eq(ba2) + expect(voter2.officer_assignment).to eq(ba2.officer_assignments.first) + end end diff --git a/spec/features/officing_spec.rb b/spec/features/officing_spec.rb index 0d865b13f..fe03d5eef 100644 --- a/spec/features/officing_spec.rb +++ b/spec/features/officing_spec.rb @@ -97,6 +97,7 @@ feature "Poll Officing" do end scenario "Poll officer access links" do + create(:poll) create(:poll_officer, user: user) login_as(user) visit root_path diff --git a/spec/features/polls/voter_spec.rb b/spec/features/polls/voter_spec.rb index 8e0271f8f..9310c2438 100644 --- a/spec/features/polls/voter_spec.rb +++ b/spec/features/polls/voter_spec.rb @@ -8,6 +8,7 @@ feature "Voter" do let(:question) { create(:poll_question, poll: poll) } let(:booth) { create(:poll_booth) } let(:officer) { create(:poll_officer) } + let(:admin) { create(:administrator) } let!(:answer_yes) { create(:poll_question_answer, question: question, title: "Yes") } let!(:answer_no) { create(:poll_question_answer, question: question, title: "No") } @@ -71,6 +72,56 @@ feature "Voter" do expect(Poll::Voter.count).to eq(1) expect(Poll::Voter.first.origin).to eq("booth") + + visit root_path + click_link "Sign out" + login_as(admin.user) + visit admin_poll_recounts_path(poll) + + within("#total_system") do + expect(page).to have_content "1" + end + + within("#poll_booth_assignment_#{Poll::BoothAssignment.where(poll: poll, booth: booth).first.id}_recounts") do + expect(page).to have_content "1" + end + end + + context "The person has decided not to vote at this time" do + let!(:user) { create(:user, :in_census) } + + scenario "Show not to vote at this time button" do + login_through_form_as_officer(officer.user) + + visit new_officing_residence_path + officing_verify_residence + + expect(page).to have_content poll.name + expect(page).to have_button "Confirm vote" + expect(page).to have_content "Can vote" + expect(page).to have_link "The person has decided not to vote at this time" + end + + scenario "Hides not to vote at this time button if already voted", :js do + login_through_form_as_officer(officer.user) + + visit new_officing_residence_path + officing_verify_residence + + within("#poll_#{poll.id}") do + click_button("Confirm vote") + expect(page).not_to have_button("Confirm vote") + expect(page).to have_content "Vote introduced!" + expect(page).not_to have_content "The person has decided not to vote at this time" + end + + visit new_officing_residence_path + officing_verify_residence + + expect(page).to have_content "Has already participated in this poll" + expect(page).not_to have_content "The person has decided not to vote at this time" + end + end context "Trying to vote the same poll in booth and web" do @@ -107,6 +158,19 @@ feature "Voter" do expect(page).not_to have_link(answer_yes.title) expect(page).to have_content "You have already participated in a physical booth. You can not participate again." expect(Poll::Voter.count).to eq(1) + + visit root_path + click_link "Sign out" + login_as(admin.user) + visit admin_poll_recounts_path(poll) + + within("#total_system") do + expect(page).to have_content "1" + end + + within("#poll_booth_assignment_#{Poll::BoothAssignment.where(poll: poll, booth: booth).first.id}_recounts") do + expect(page).to have_content "1" + end end scenario "Trying to vote in web again", :js do @@ -162,6 +226,19 @@ feature "Voter" do expect(page).not_to have_link(answer_yes.title) expect(page).to have_content "You have already participated in a physical booth. You can not participate again." expect(Poll::Voter.count).to eq(1) + + visit root_path + click_link "Sign out" + login_as(admin.user) + visit admin_poll_recounts_path(poll) + + within("#total_system") do + expect(page).to have_content "1" + end + + within("#poll_booth_assignment_#{Poll::BoothAssignment.where(poll: poll, booth: booth).first.id}_recounts") do + expect(page).to have_content "1" + end end context "Side menu" do diff --git a/spec/models/budget/investment_spec.rb b/spec/models/budget/investment_spec.rb index 7c2b0a980..044dead99 100644 --- a/spec/models/budget/investment_spec.rb +++ b/spec/models/budget/investment_spec.rb @@ -1131,4 +1131,91 @@ describe Budget::Investment do end end + + describe "scoped_filter" do + let(:budget) { create(:budget, phase: "balloting") } + let(:investment) { create(:budget_investment, budget: budget) } + + describe "with without_admin filter" do + let(:params) { {advanced_filters: ["without_admin"], budget_id: budget.id} } + it "returns only investment without admin" do + create(:budget_investment, + :finished, + budget: budget) + create(:budget_investment, + :with_administrator, + budget: budget) + investment3 = create(:budget_investment, budget: budget) + expect(described_class.scoped_filter(params, "all")).to eq([investment3]) + expect(described_class.scoped_filter(params, "all").count).to eq(1) + end + end + + describe "with without_valuator filter" do + let(:params) { {advanced_filters: ["without_valuator"], budget_id: budget.id} } + it "returns only investment without valuator" do + create(:budget_investment, + :finished, + budget: budget) + investment2 = create(:budget_investment, + :with_administrator, + budget: budget) + investment3 = create(:budget_investment, + budget: budget) + expect(described_class.scoped_filter(params, "all")) + .to contain_exactly(investment2, investment3) + expect(described_class.scoped_filter(params, "all").count) + .to eq(2) + end + end + + describe "with under_valuation filter" do + let(:params) { {advanced_filters: ["under_valuation"], budget_id: budget.id} } + it "returns only investment under valuation" do + valuator1 = create(:valuator) + investment1 = create(:budget_investment, + :with_administrator, + valuation_finished: false, + budget: budget) + investment1.valuators << valuator1 + create(:budget_investment, :with_administrator, budget: budget) + create(:budget_investment, budget: budget) + + expect(described_class.scoped_filter(params, "all")).to eq([investment1]) + expect(described_class.scoped_filter(params, "all").count).to eq(1) + end + end + + describe "with valuation_finished filter" do + let(:params) { {advanced_filters: ["valuation_finished"], budget_id: budget.id} } + it "returns only investment with valuation finished" do + investment1 = create(:budget_investment, + :selected, + budget: budget) + create(:budget_investment, + :with_administrator, + budget: budget) + create(:budget_investment, + budget: budget) + expect(described_class.scoped_filter(params, "all")).to eq([investment1]) + expect(described_class.scoped_filter(params, "all").count).to eq(1) + end + end + + describe "with winners filter" do + let(:params) { {advanced_filters: ["winners"], budget_id: budget.id} } + it "returns only investment winners" do + investment1 = create(:budget_investment, + :winner, + valuation_finished: true, + budget: budget) + create(:budget_investment, + :with_administrator, + budget: budget) + create(:budget_investment, budget: budget) + expect(described_class.scoped_filter(params, "all")).to eq([investment1]) + expect(described_class.scoped_filter(params, "all").count).to eq(1) + end + end + end end diff --git a/spec/models/poll/officer_spec.rb b/spec/models/poll/officer_spec.rb index 1cbad6f5d..4cdc581b1 100644 --- a/spec/models/poll/officer_spec.rb +++ b/spec/models/poll/officer_spec.rb @@ -121,4 +121,21 @@ describe Poll::Officer do expect(assigned_polls.last).to eq(poll_3) end end + + describe "todays_booths" do + let(:officer) { create(:poll_officer) } + + it "returns booths for the application's time zone date", :with_different_time_zone do + assignment_with_local_time_zone = create(:poll_officer_assignment, + date: Date.today, + officer: officer) + + assignment_with_application_time_zone = create(:poll_officer_assignment, + date: Date.current, + officer: officer) + + expect(officer.todays_booths).to include(assignment_with_application_time_zone.booth) + expect(officer.todays_booths).not_to include(assignment_with_local_time_zone.booth) + end + end end diff --git a/spec/models/poll/voter_spec.rb b/spec/models/poll/voter_spec.rb index 85c72aa7d..3ebab9818 100644 --- a/spec/models/poll/voter_spec.rb +++ b/spec/models/poll/voter_spec.rb @@ -6,6 +6,7 @@ describe Poll::Voter do let(:booth) { create(:poll_booth) } let(:booth_assignment) { create(:poll_booth_assignment, poll: poll, booth: booth) } let(:voter) { create(:poll_voter) } + let(:officer_assignment) { create(:poll_officer_assignment) } describe "validations" do @@ -97,6 +98,7 @@ describe Poll::Voter do it "is valid with a booth origin" do voter.origin = "booth" + voter.officer_assignment = officer_assignment expect(voter).to be_valid end @@ -104,7 +106,27 @@ describe Poll::Voter do voter.origin = "web" expect(voter).to be_valid end + end + context "assignments" do + it "should not be valid without a booth_assignment_id when origin is booth" do + voter.origin = "booth" + voter.booth_assignment_id = nil + expect(voter).not_to be_valid + end + + it "should not be valid without an officer_assignment_id when origin is booth" do + voter.origin = "booth" + voter.officer_assignment_id = nil + expect(voter).not_to be_valid + end + + it "should be valid without assignments when origin is web" do + voter.origin = "web" + voter.booth_assignment_id = nil + voter.officer_assignment_id = nil + expect(voter).to be_valid + end end end diff --git a/spec/support/common_actions.rb b/spec/support/common_actions.rb index 4972d215e..118508119 100644 --- a/spec/support/common_actions.rb +++ b/spec/support/common_actions.rb @@ -21,4 +21,15 @@ module CommonActions check "user_terms_of_service" end + def validate_officer + allow_any_instance_of(Officing::BaseController). + to receive(:verify_officer_assignment).and_return(true) + end + + def set_officing_booth(booth=nil) + booth = create(:poll_booth) if booth.blank? + + allow_any_instance_of(Officing::BaseController). + to receive(:current_booth).and_return(booth) + end end