diff --git a/app/controllers/officing/ballot_sheets_controller.rb b/app/controllers/officing/ballot_sheets_controller.rb
index e23ea4613..9a4f08401 100644
--- a/app/controllers/officing/ballot_sheets_controller.rb
+++ b/app/controllers/officing/ballot_sheets_controller.rb
@@ -19,13 +19,13 @@ class Officing::BallotSheetsController < Officing::BaseController
def create
load_officer_assignment
- check_officer_assignment
@ballot_sheet = Poll::BallotSheet.new(ballot_sheet_params)
if @ballot_sheet.save
redirect_to officing_poll_ballot_sheet_path(@poll, @ballot_sheet)
else
+ flash.now[:alert] = @ballot_sheet.errors.full_messages.join(", ")
render :new
end
end
@@ -63,13 +63,6 @@ class Officing::BallotSheetsController < Officing::BaseController
.find_by(id: ballot_sheet_params[:officer_assignment_id])
end
- def check_officer_assignment
- if @officer_assignment.blank?
- flash.now[:alert] = t("officing.results.flash.error_wrong_booth")
- render :new
- end
- end
-
def ballot_sheet_params
params.permit(:data, :poll_id, :officer_assignment_id)
end
diff --git a/app/controllers/officing/poll/ballot_sheets_controller.rb b/app/controllers/officing/poll/ballot_sheets_controller.rb
deleted file mode 100644
index 344be299f..000000000
--- a/app/controllers/officing/poll/ballot_sheets_controller.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-class Officing::PollBallotSheetsController < Officing::BaseController
-
- before_action :verify_booth
- before_action :load_poll
- before_action :load_ballot_sheets, only: :index
- before_action :load_ballot_sheet, only: :show
-
- before_action :load_officer_assignments, only: :new
- before_action :load_officer_assignment, only: :create
- before_action :check_officer_assignment, only: :create
-
- helper_method :namespace
-
- def index
- end
-
- def show
- end
-
- def new
- end
-
- def create
- Poll::BallotSheet.create(ballot_sheet_params)
-
- render :show
- end
-
- private
-
- def namespace
- "officing"
- end
-
- def load_poll
- @poll = Poll.find(params[:poll_id])
- end
-
- def load_ballot_sheets
- @ballot_sheets = Poll::BallotSheet.where(poll: @poll)
- end
-
- def load_ballot_sheet
- @ballot_sheet = Poll::BallotSheet.find(params[:ballot_sheet_id])
- end
-
- def load_officer_assignments
- @officer_assignments = ::Poll::OfficerAssignment.
- includes(booth_assignment: [:booth]).
- joins(:booth_assignment).
- final.
- where(id: current_user.poll_officer.officer_assignment_ids).
- where("poll_booth_assignments.poll_id = ?", @poll_budget.id).
- where(date: Date.current)
- end
-
- def ballot_sheet_params
- params.permit(:csv_data, :poll_id, :officer_assignment_id)
- end
-
-end
diff --git a/app/models/poll.rb b/app/models/poll.rb
index 553baf036..012fce912 100644
--- a/app/models/poll.rb
+++ b/app/models/poll.rb
@@ -20,6 +20,7 @@ class Poll < ActiveRecord::Base
has_many :officers, through: :officer_assignments
has_many :questions
has_many :comments, as: :commentable
+ has_many :ballot_sheets
has_and_belongs_to_many :geozones
belongs_to :author, -> { with_hidden }, class_name: "User", foreign_key: "author_id"
diff --git a/app/models/poll/ballot_sheet.rb b/app/models/poll/ballot_sheet.rb
new file mode 100644
index 000000000..24c3c38b8
--- /dev/null
+++ b/app/models/poll/ballot_sheet.rb
@@ -0,0 +1,12 @@
+class Poll::BallotSheet < ActiveRecord::Base
+ belongs_to :poll
+ belongs_to :officer_assignment
+
+ validates :data, presence: true
+ validates :poll_id, presence: true
+ validates :officer_assignment_id, presence: true
+
+ def author
+ officer_assignment.officer.name
+ end
+end
diff --git a/app/models/poll/officer_assignment.rb b/app/models/poll/officer_assignment.rb
index 7de4f98b3..c997c9a5a 100644
--- a/app/models/poll/officer_assignment.rb
+++ b/app/models/poll/officer_assignment.rb
@@ -2,6 +2,7 @@ class Poll
class OfficerAssignment < ActiveRecord::Base
belongs_to :officer
belongs_to :booth_assignment
+ has_many :ballot_sheets
has_many :partial_results
has_many :recounts
has_many :voters
diff --git a/app/views/officing/_menu.html.erb b/app/views/officing/_menu.html.erb
index 17b57a087..5630702c0 100644
--- a/app/views/officing/_menu.html.erb
+++ b/app/views/officing/_menu.html.erb
@@ -12,7 +12,7 @@
<% end %>
<% if final_recount_shift? %>
-
>
+ >
<%= link_to final_officing_polls_path do %>
<%= t("officing.menu.total_recounts") %>
diff --git a/app/views/officing/ballot_sheets/index.html.erb b/app/views/officing/ballot_sheets/index.html.erb
new file mode 100644
index 000000000..ce1cd7db1
--- /dev/null
+++ b/app/views/officing/ballot_sheets/index.html.erb
@@ -0,0 +1,27 @@
+<%= t("officing.poll_budgets.index.title", poll_budget: @poll.name) %>
+
+<% if @ballot_sheets.any? %>
+
+
+
+ | <%= t("officing.poll_budgets.index.ballot_sheet_name") %> |
+ <%= t("officing.poll_budgets.index.ballot_sheet_author") %> |
+ <%= t("officing.poll_budgets.index.ballot_sheet_creation_date") %> |
+
+
+
+ <% @ballot_sheets.each do |ballot_sheet| %>
+
+ | <%= link_to t("officing.poll_budgets.index.ballot_sheet", id: ballot_sheet.id),
+ officing_poll_ballot_sheet_path(@poll.id, ballot_sheet.id) %> |
+ <%= ballot_sheet.author %> |
+ <%= l(ballot_sheet.created_at, format: :long) %> |
+
+ <% end %>
+
+
+<% else %>
+
+ <%= t("officing.poll_budgets.index.empty_results") %>
+
+<% end %>
diff --git a/app/views/officing/polls/ballot_sheets/new.html.erb b/app/views/officing/ballot_sheets/new.html.erb
similarity index 74%
rename from app/views/officing/polls/ballot_sheets/new.html.erb
rename to app/views/officing/ballot_sheets/new.html.erb
index 335477ec3..a83c3a7d0 100644
--- a/app/views/officing/polls/ballot_sheets/new.html.erb
+++ b/app/views/officing/ballot_sheets/new.html.erb
@@ -1,15 +1,14 @@
<% if @officer_assignments.any? %>
- <%= t("officing.poll_budgets.new.title", poll: @poll.name) %>
+ <%= t("officing.poll_budgets.new.title", poll_budget: @poll.name) %>
<%= form_tag(officing_poll_ballot_sheets_path(@poll)) do %>
<%= select_tag :officer_assignment_id,
booths_for_officer_select_options(@officer_assignments),
- { prompt: t("officing.poll_budgets.new.select_booth"),
- label: false } %>
+ { prompt: t("officing.poll_budgets.new.select_booth"), label: false } %>
- <%= text_area_tag :csv_data, nil, rows: 10 %>
+ <%= text_area_tag :data, nil, rows: 10 %>
<%= submit_tag t("officing.poll_budgets.new.submit"), class: "button" %>
<% end %>
diff --git a/app/views/officing/ballot_sheets/show.html.erb b/app/views/officing/ballot_sheets/show.html.erb
new file mode 100644
index 000000000..6a111f353
--- /dev/null
+++ b/app/views/officing/ballot_sheets/show.html.erb
@@ -0,0 +1,14 @@
+<%= t("officing.poll_budgets.index.ballot_sheet", id: @ballot_sheet.id) %>
+
+
+ <%= t("officing.poll_budgets.show.created_at") %>
+ <%= l(@ballot_sheet.created_at, format: :long) %>
+ •
+ <%= t("officing.poll_budgets.show.author") %>
+ <%= @ballot_sheet.author %>
+
+
+
+
<%= t("officing.poll_budgets.show.data") %>
+ <%= simple_format @ballot_sheet.data %>
+
diff --git a/app/views/officing/polls/ballot_sheets/index.html.erb b/app/views/officing/polls/ballot_sheets/index.html.erb
deleted file mode 100644
index 2dc905b4f..000000000
--- a/app/views/officing/polls/ballot_sheets/index.html.erb
+++ /dev/null
@@ -1,26 +0,0 @@
-<%= t("officing.poll_budgets.index.title", poll_budget: @poll_budget) %>
-
-<% if @ballot_sheets.any? %>
-
-
-
- | <%= t("officing.poll_budgets.index.ballot_sheet_name") %> |
- <%= t("officing.poll_budgets.index.ballot_sheet_author") %> |
- <%= t("officing.poll_budgets.index.ballot_sheet_creation_date") %> |
-
-
-
- <% @ballot_sheets.each do |ballot_sheet| %>
-
- |
- |
- |
-
- <% end %>
-
-
-<% else %>
-
- <%= t("officing.poll_budgets.index.empty_results") %>
-
-<% end %>
diff --git a/app/views/officing/polls/ballot_sheets/show.html.erb b/app/views/officing/polls/ballot_sheets/show.html.erb
deleted file mode 100644
index e69de29bb..000000000
diff --git a/config/locales/en/officing.yml b/config/locales/en/officing.yml
index b40f7c448..91b1aeb4e 100644
--- a/config/locales/en/officing.yml
+++ b/config/locales/en/officing.yml
@@ -20,6 +20,7 @@ en:
see_ballot_sheets: See ballot sheets list
index:
title: "%{poll_budget} - Ballot sheets list"
+ ballot_sheet: "Ballot sheet %{id}"
ballot_sheet_name: Name
ballot_sheet_author: Author
ballot_sheet_creation_date: Creation date
@@ -30,6 +31,10 @@ en:
select_booth: Select booth
csv_data: CSV data
submit: Save
+ show:
+ created_at: Creation date
+ author: Author
+ data: CSV data
booth:
new:
title: "Choose your booth"
diff --git a/config/locales/es/officing.yml b/config/locales/es/officing.yml
index 103a25982..94f75ae25 100644
--- a/config/locales/es/officing.yml
+++ b/config/locales/es/officing.yml
@@ -20,6 +20,7 @@ es:
see_ballot_sheets: Ver lista de papeletas de votación
index:
title: "%{poll_budget} - Listado de papeletas de votación"
+ ballot_sheet: "Papeleta de votación %{id}"
ballot_sheet_name: Nombre
ballot_sheet_author: Autor
ballot_sheet_creation_date: Fecha de creación
@@ -30,6 +31,10 @@ es:
select_booth: Elegir urna
csv_data: Datos de CSV
submit: Guardar
+ show:
+ created_at: Fecha de creación
+ author: Autor
+ data: Datos de CSV
booth:
new:
title: "Escoge tu urna"
diff --git a/db/migrate/20180621182723_create_poll_ballot_sheets.rb b/db/migrate/20180621182723_create_poll_ballot_sheets.rb
new file mode 100644
index 000000000..ee06c5724
--- /dev/null
+++ b/db/migrate/20180621182723_create_poll_ballot_sheets.rb
@@ -0,0 +1,10 @@
+class CreatePollBallotSheets < ActiveRecord::Migration
+ def change
+ create_table :poll_ballot_sheets do |t|
+ t.text :data
+ t.integer :poll_id, index: true
+ t.integer :officer_assignment_id, index: true
+ t.timestamps null: false
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 96e3d3487..d65c89471 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -927,6 +927,17 @@ ActiveRecord::Schema.define(version: 20190205131722) do
add_index "poll_answers", ["question_id", "answer"], name: "index_poll_answers_on_question_id_and_answer", using: :btree
add_index "poll_answers", ["question_id"], name: "index_poll_answers_on_question_id", using: :btree
+ create_table "poll_ballot_sheets", force: :cascade do |t|
+ t.text "data"
+ t.integer "poll_id"
+ t.integer "officer_assignment_id"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ add_index "poll_ballot_sheets", ["officer_assignment_id"], name: "index_poll_ballot_sheets_on_officer_assignment_id", using: :btree
+ add_index "poll_ballot_sheets", ["poll_id"], name: "index_poll_ballot_sheets_on_poll_id", using: :btree
+
create_table "poll_booth_assignments", force: :cascade do |t|
t.integer "booth_id"
t.integer "poll_id"
diff --git a/spec/factories/polls.rb b/spec/factories/polls.rb
index cf62728d2..895d723a9 100644
--- a/spec/factories/polls.rb
+++ b/spec/factories/polls.rb
@@ -147,6 +147,12 @@ FactoryBot.define do
end
end
+ factory :poll_ballot_sheet, class: "Poll::BallotSheet" do
+ association :poll
+ association :officer_assignment, factory: :poll_officer_assignment
+ data "1234;9876;5678\n1000;2000;3000;9999"
+ end
+
factory :officing_residence, class: "Officing::Residence" do
user
association :officer, factory: :poll_officer
diff --git a/spec/features/budget_polls/ballot_sheets_spec.rb b/spec/features/budget_polls/ballot_sheets_spec.rb
index a20921126..cc42e253d 100644
--- a/spec/features/budget_polls/ballot_sheets_spec.rb
+++ b/spec/features/budget_polls/ballot_sheets_spec.rb
@@ -6,27 +6,169 @@ feature "Poll budget ballot sheets" do
let(:booth) { create(:poll_booth) }
let(:poll_officer) { create(:poll_officer) }
- background do
- create(:poll_booth_assignment, poll: poll, booth: booth)
- create(:poll_shift, :recount_scrutiny_task, officer: poll_officer, booth: booth, date: Date.current)
- create(:poll_officer_assignment, officer: poll_officer)
+ context "Officing recounts and results view" do
+
+ background do
+ create(:poll_booth_assignment, poll: poll, booth: booth)
+ create(:poll_shift, :recount_scrutiny_task, officer: poll_officer, booth: booth,
+ date: Date.current)
+ create(:poll_officer_assignment, officer: poll_officer)
+
+ login_as(poll_officer.user)
+ set_officing_booth(booth)
+ end
+
+ scenario "Budget polls are visible" do
+ visit root_path
+ click_link "Polling officers"
+
+ within("#side_menu") do
+ click_link "Total recounts and results"
+ end
+
+ within("#poll_#{poll.id}") do
+ expect(page).to have_content("#{poll.name}")
+ expect(page).to have_content("See ballot sheets list")
+ expect(page).to have_content("Add results")
+ end
+ end
- login_as(poll_officer.user)
- set_officing_booth(booth)
end
- scenario "Budget polls are visible in 'Recounts and results' view" do
- visit root_path
- click_link "Polling officers"
+ context "Booth assignment" do
- within("#side_menu") do
- click_link "Total recounts and results"
+ scenario "Try to access ballot sheets officing without booth assignment" do
+ login_as(poll_officer.user)
+ visit officing_poll_ballot_sheets_path(poll)
+
+ expect(page).to have_content "You don't have officing shifts today"
end
- within("#poll_#{poll.id}") do
- expect(page).to have_content("#{poll.name}")
- expect(page).to have_content("See ballot sheets list")
- expect(page).to have_content("Add results")
+ scenario "Access ballot sheets officing with one booth assignment" do
+ create(:poll_booth_assignment, poll: poll, booth: booth)
+ create(:poll_shift, :recount_scrutiny_task, officer: poll_officer, booth: booth,
+ date: Date.current)
+ create(:poll_officer_assignment, officer: poll_officer)
+
+ login_as(poll_officer.user)
+ set_officing_booth(booth)
+
+ visit officing_poll_ballot_sheets_path(poll)
+
+ expect(page).to have_content "#{poll.name}"
end
+
+ scenario "Access ballot sheets officing with multiple booth assignments", :with_frozen_time do
+ booth_2 = create(:poll_booth)
+ create(:poll_booth_assignment, poll: poll, booth: booth)
+ create(:poll_booth_assignment, poll: poll, booth: booth_2)
+ create(:poll_shift, :recount_scrutiny_task, officer: poll_officer, booth: booth,
+ date: Date.current)
+ create(:poll_shift, :recount_scrutiny_task, officer: poll_officer, booth: booth_2,
+ date: Date.current)
+ create(:poll_officer_assignment, officer: poll_officer)
+ create(:poll_officer_assignment, officer: poll_officer)
+
+ login_as(poll_officer.user)
+
+ visit officing_poll_ballot_sheets_path(poll)
+
+ expect(page).to have_content "Choose your booth"
+ end
+
+ end
+
+ context "Index" do
+
+ background do
+ create(:poll_booth_assignment, poll: poll, booth: booth)
+ create(:poll_shift, :recount_scrutiny_task, officer: poll_officer, booth: booth,
+ date: Date.current)
+
+ login_as(poll_officer.user)
+ set_officing_booth(booth)
+ end
+
+ scenario "Ballot sheets are listed" do
+ officer_assignment = create(:poll_officer_assignment, officer: poll_officer)
+ ballot_sheet = create(:poll_ballot_sheet, poll: poll, officer_assignment: officer_assignment)
+
+ visit officing_poll_ballot_sheets_path(poll)
+
+ expect(page).to have_content "Ballot sheet #{ballot_sheet.id}"
+ end
+
+ end
+
+ context "New" do
+
+ background do
+ create(:poll_booth_assignment, poll: poll, booth: booth)
+ create(:poll_shift, :recount_scrutiny_task, officer: poll_officer, booth: booth,
+ date: Date.current)
+ create(:poll_officer_assignment, officer: poll_officer)
+
+ login_as(poll_officer.user)
+ set_officing_booth(booth)
+ end
+
+ scenario "Ballot sheet is saved" do
+ visit new_officing_poll_ballot_sheet_path(poll)
+
+ select "#{booth.name}", from: "officer_assignment_id"
+ fill_in "data", with: "1234;5678"
+ click_button "Save"
+
+ expect(Poll::BallotSheet.count).to be 1
+
+ expect(page).to have_content("Ballot sheet #{Poll::BallotSheet.last.id}")
+ expect(page).to have_content(poll_officer.user.name)
+ expect(page).to have_content("1234;5678")
+ end
+
+ scenario "Ballot sheet is not saved" do
+ visit new_officing_poll_ballot_sheet_path(poll)
+
+ select "#{booth.name}", from: "officer_assignment_id"
+ click_button "Save"
+
+ expect(Poll::BallotSheet.count).to be 0
+
+ expect(page).to have_content("CSV data can't be blank")
+ end
+
+ scenario "Shift booth has to be selected", :js do
+ visit new_officing_poll_ballot_sheet_path(poll)
+
+ fill_in "data", with: "1234;5678"
+ click_button "Save"
+
+ expect(page).to have_content "Officer assignment can't be blank"
+ end
+
+ end
+
+ context "Show" do
+
+ background do
+ create(:poll_booth_assignment, poll: poll, booth: booth)
+ create(:poll_shift, :recount_scrutiny_task, officer: poll_officer, booth: booth,
+ date: Date.current)
+
+ login_as(poll_officer.user)
+ set_officing_booth(booth)
+ end
+
+ scenario "Ballot sheet information is displayed" do
+ officer_assignment = create(:poll_officer_assignment, officer: poll_officer)
+ ballot_sheet = create(:poll_ballot_sheet, poll: poll, officer_assignment: officer_assignment)
+
+ visit officing_poll_ballot_sheet_path(poll, ballot_sheet)
+
+ expect(page).to have_content("Ballot sheet #{ballot_sheet.id}")
+ expect(page).to have_content(ballot_sheet.author)
+ expect(page).to have_content(ballot_sheet.data)
+ end
+
end
end
diff --git a/spec/models/poll/ballot_sheet_spec.rb b/spec/models/poll/ballot_sheet_spec.rb
new file mode 100644
index 000000000..2ad0e0d5d
--- /dev/null
+++ b/spec/models/poll/ballot_sheet_spec.rb
@@ -0,0 +1,40 @@
+require "rails_helper"
+
+describe Poll::BallotSheet do
+
+ let(:ballot_sheet) { build(:poll_ballot_sheet, poll: create(:poll),
+ officer_assignment: create(:poll_officer_assignment),
+ data: "1234;5678") }
+
+ context "Validations" do
+
+ it "is valid" do
+ expect(ballot_sheet).to be_valid
+ end
+
+ it "is not valid without a poll" do
+ ballot_sheet.poll = nil
+ expect(ballot_sheet).not_to be_valid
+ end
+
+ it "is not valid without an officer assignment" do
+ ballot_sheet.officer_assignment = nil
+ expect(ballot_sheet).not_to be_valid
+ end
+
+ it "is not valid without data" do
+ ballot_sheet.data = nil
+ expect(ballot_sheet).not_to be_valid
+ end
+
+ end
+
+ context "#author" do
+
+ it "returns the officer's name" do
+ expect(ballot_sheet.author).to be(ballot_sheet.officer_assignment.officer.user.name)
+ end
+
+ end
+
+end