diff --git a/app/controllers/admin/poll/shifts_controller.rb b/app/controllers/admin/poll/shifts_controller.rb
new file mode 100644
index 000000000..8a808a7a9
--- /dev/null
+++ b/app/controllers/admin/poll/shifts_controller.rb
@@ -0,0 +1,53 @@
+class Admin::Poll::ShiftsController < Admin::BaseController
+
+ before_action :load_booth
+ before_action :load_polls
+
+ def new
+ load_officers
+ load_shifts
+ @shift = ::Poll::Shift.new
+ end
+
+ def create
+ @shift = ::Poll::Shift.new(shift_params)
+ if @shift.save
+ notice = t("admin.poll_shifts.flash.create")
+ redirect_to new_admin_booth_shift_path(@shift.booth), notice: notice
+ else
+ load_officers
+ load_shifts
+ render :new
+ end
+ end
+
+ def destroy
+ @shift = Poll::Shift.find(params[:id])
+ @shift.destroy
+ notice = t("admin.poll_shifts.flash.destroy")
+ redirect_to new_admin_booth_shift_path(@booth), notice: notice
+ end
+
+ private
+
+ def load_booth
+ @booth = ::Poll::Booth.find(params[:booth_id])
+ end
+
+ def load_polls
+ @polls = ::Poll.current_or_incoming
+ end
+
+ def load_officers
+ @officers = ::Poll::Officer.all
+ end
+
+ def load_shifts
+ @shifts = @booth.shifts
+ end
+
+ def shift_params
+ params.require(:shift).permit(:booth_id, :officer_id, :date)
+ end
+
+end
\ No newline at end of file
diff --git a/app/helpers/shifts_helper.rb b/app/helpers/shifts_helper.rb
new file mode 100644
index 000000000..37f22a3e2
--- /dev/null
+++ b/app/helpers/shifts_helper.rb
@@ -0,0 +1,23 @@
+module ShiftsHelper
+
+ def shift_dates_select_options(polls)
+ options = []
+ (start_date(polls)..end_date(polls)).each do |date|
+ options << [l(date, format: :long), l(date)]
+ end
+ options_for_select(options, params[:date])
+ end
+
+ def start_date(polls)
+ polls.map(&:starts_at).min.to_date
+ end
+
+ def end_date(polls)
+ polls.map(&:ends_at).max.to_date
+ end
+
+ def officer_select_options(officers)
+ officers.collect { |officer| [officer.name, officer.id] }
+ end
+
+end
diff --git a/app/models/poll.rb b/app/models/poll.rb
index 6d033f514..4ba313963 100644
--- a/app/models/poll.rb
+++ b/app/models/poll.rb
@@ -35,6 +35,10 @@ class Poll < ActiveRecord::Base
ends_at < timestamp
end
+ def self.current_or_incoming
+ current + incoming
+ end
+
def answerable_by?(user)
user.present? &&
user.level_two_or_three_verified? &&
diff --git a/app/models/poll/booth.rb b/app/models/poll/booth.rb
index c7fb63efc..9edbcbaf0 100644
--- a/app/models/poll/booth.rb
+++ b/app/models/poll/booth.rb
@@ -2,6 +2,7 @@ class Poll
class Booth < ActiveRecord::Base
has_many :booth_assignments, class_name: "Poll::BoothAssignment"
has_many :polls, through: :booth_assignments
+ has_many :shifts
validates :name, presence: true, uniqueness: true
diff --git a/app/models/poll/shift.rb b/app/models/poll/shift.rb
new file mode 100644
index 000000000..8ee646ea4
--- /dev/null
+++ b/app/models/poll/shift.rb
@@ -0,0 +1,22 @@
+class Poll
+ class Shift < ActiveRecord::Base
+ belongs_to :booth
+ belongs_to :officer
+
+ validates :booth_id, presence: true
+ validates :officer_id, presence: true
+ validates :date, presence: true
+ validates :date, uniqueness: { scope: [:officer_id, :booth_id] }
+
+ after_create :create_officer_assignments
+
+ def create_officer_assignments
+ booth.booth_assignments.each do |booth_assignment|
+ attrs = { officer_id: officer_id,
+ date: date,
+ booth_assignment_id: booth_assignment.id }
+ Poll::OfficerAssignment.create!(attrs)
+ end
+ end
+ end
+ end
\ No newline at end of file
diff --git a/app/views/admin/poll/booths/_booth.html.erb b/app/views/admin/poll/booths/_booth.html.erb
index 5732400a8..80b1ef38f 100644
--- a/app/views/admin/poll/booths/_booth.html.erb
+++ b/app/views/admin/poll/booths/_booth.html.erb
@@ -6,6 +6,9 @@
<%= booth.location %>
+ <%= link_to t("admin.booths.booth.shifts"),
+ new_admin_booth_shift_path(booth),
+ class: "button hollow" %>
<%= link_to t("admin.actions.edit"),
edit_admin_booth_path(booth),
class: "button hollow" %>
diff --git a/app/views/admin/poll/booths/index.html.erb b/app/views/admin/poll/booths/index.html.erb
index 9618aec59..0dbb62cdf 100644
--- a/app/views/admin/poll/booths/index.html.erb
+++ b/app/views/admin/poll/booths/index.html.erb
@@ -16,6 +16,7 @@
| <%= t("admin.booths.index.name") %> |
<%= t("admin.booths.index.location") %> |
|
+ |
<% @booths.each do |booth| %>
diff --git a/app/views/admin/poll/shifts/_shifts.html.erb b/app/views/admin/poll/shifts/_shifts.html.erb
new file mode 100644
index 000000000..800c6944b
--- /dev/null
+++ b/app/views/admin/poll/shifts/_shifts.html.erb
@@ -0,0 +1,24 @@
+<%= t("admin.poll_shifts.new.assignments") %>
+
+
+
+ | <%= t("admin.poll_shifts.new.date") %> |
+ <%= t("admin.poll_shifts.new.officer") %> |
+ <%= t("admin.poll_shifts.new.assignment") %> |
+
+
+
+ <% @shifts.each do |shift| %>
+
+ | <%= l(shift.date.to_date, format: :long) %> |
+ <%= shift.officer.name %> |
+
+ <%= link_to t("admin.poll_shifts.new.remove_assignment"),
+ admin_booth_shift_path(@booth, shift),
+ method: :delete,
+ class: "button hollow alert" %>
+ |
+
+ <% end %>
+
+
diff --git a/app/views/admin/poll/shifts/new.html.erb b/app/views/admin/poll/shifts/new.html.erb
new file mode 100644
index 000000000..c997dc35f
--- /dev/null
+++ b/app/views/admin/poll/shifts/new.html.erb
@@ -0,0 +1,46 @@
+<%= back_link_to admin_booths_path %>
+
+<%= @booth.name %>
+
+<%= form_for @shift, as: :shift, url: admin_booth_shifts_path do |f| %>
+ <%= render "shared/errors", resource: @shift %>
+
+
+<% end %>
+
+
+ <% if @shifts.empty? %>
+
+ <%= t("admin.poll_shifts.new.no_assignments") %>
+
+ <% else %>
+ <%= render "shifts" %>
+ <% end %>
+
\ No newline at end of file
diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml
index 69ffa296e..dea45c3a3 100644
--- a/config/locales/en/admin.yml
+++ b/config/locales/en/admin.yml
@@ -403,6 +403,7 @@ en:
poll_officers: Poll officers
polls: Polls
poll_booths: Booths location
+ poll_shifts: Manage shifts
officials: Officials
organizations: Organisations
settings: Configuration settings
@@ -481,11 +482,6 @@ en:
search: Search
user_not_found: User not found
poll_officer_assignments:
- flash:
- destroy: "Officing shift removed"
- create: "Officing shift added"
- error_destroy: "An error ocurred when removing officer assignment"
- error_create: "An error ocurred when adding officer assignment"
index:
officers_title: "List of officers"
no_officers: "There are no officers assigned to this poll."
@@ -494,18 +490,27 @@ en:
add_officer_assignments: "Add shifts as officer"
edit_officer_assignments: "Edit officing shifts"
by_officer:
- new_assignment: "New shift"
date: "Date"
booth: "Booth"
- assignment: "Assignment"
- select_date: "Select day"
- select_booth: "Select booth"
- add_assignment: "Add shift"
- remove_assignment: "Remove"
assignments: "Officing shifts in this poll"
no_assignments: "This user has no officing shifts in this poll."
final_recounts: "Final recounts"
final_recount: "Final recount (by officer)"
+ poll_shifts:
+ new:
+ new_assignment: "New shift"
+ date: "Date"
+ officer: "Officer"
+ assignment: "Assignment"
+ select_date: "Select day"
+ select_officer: "Select officer"
+ add_assignment: "Add shift"
+ remove_assignment: "Remove"
+ assignments: "Shifts in this booth"
+ no_assignments: "This booth has no shifts"
+ flash:
+ create: "Shift added"
+ destroy: "Shift removed"
poll_booth_assignments:
flash:
destroy: "Booth not assigned anymore"
@@ -618,6 +623,8 @@ en:
submit_button: "Update booth"
show:
location: "Location"
+ booth:
+ shifts: "Manage shifts"
officials:
edit:
destroy: Remove 'Official' status
diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml
index 280033353..6e4fb2abf 100644
--- a/config/locales/es/admin.yml
+++ b/config/locales/es/admin.yml
@@ -414,6 +414,7 @@ es:
poll_officers: Presidentes de mesa
polls: Votaciones
poll_booths: Ubicación de urnas
+ poll_shifts: Asignar turnos
officials: Cargos públicos
organizations: Organizaciones
settings: Configuración global
@@ -481,11 +482,6 @@ es:
search: Buscar
user_not_found: Usuario no encontrado
poll_officer_assignments:
- flash:
- destroy: "Eliminado turno de presidente de mesa"
- create: "Añadido turno de presidente de mesa"
- error_destroy: "Se ha producido un error al eliminar el turno"
- error_create: "Se ha producido un error al intentar crear el turno"
index:
officers_title: "Listado de presidentes de mesa asignados"
no_officers: "No hay presidentes de mesa asignados a esta votación."
@@ -494,18 +490,27 @@ es:
add_officer_assignments: "Añadir turnos como presidente de mesa"
edit_officer_assignments: "Editar turnos"
by_officer:
- new_assignment: "Nuevo turno"
date: "Fecha"
booth: "Urna"
- assignment: "Asignación"
- select_date: "Seleccionar día"
- select_booth: "Seleccionar urna"
- add_assignment: "Añadir turno"
- remove_assignment: "Eliminar turno"
assignments: "Turnos como presidente de mesa en esta votación"
no_assignments: "No tiene turnos como presidente de mesa en esta votación."
final_recounts: "Recuentos finales"
final_recount: "Recuento final (presidente de mesa)"
+ poll_shifts:
+ new:
+ new_assignment: "Nuevo turno"
+ date: "Fecha"
+ officer: "Presidente de mesa"
+ assignment: "Asignación"
+ select_date: "Seleccionar día"
+ select_officer: "Seleccionar presidente de mesa"
+ add_assignment: "Añadir turno"
+ remove_assignment: "Eliminar turno"
+ assignments: "Turnos en esta urna"
+ no_assignments: "Esta urna no tiene turnos asignados"
+ flash:
+ create: "Añadido turno de presidente de mesa"
+ destroy: "Eliminado turno de presidente de mesa"
poll_booth_assignments:
flash:
destroy: "Urna desasignada"
@@ -618,6 +623,8 @@ es:
submit_button: "Actualizar urna"
show:
location: "Ubicación"
+ booth:
+ shifts: "Asignar turnos"
officials:
edit:
destroy: Eliminar condición de 'Cargo Público'
diff --git a/config/routes.rb b/config/routes.rb
index d151fdb6c..e3d3b5bcb 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -273,7 +273,10 @@ Rails.application.routes.draw do
get :search, on: :collection
end
- resources :booths
+ resources :booths do
+ resources :shifts
+ end
+
resources :questions
end
diff --git a/db/migrate/20170724190805_create_poll_shifts.rb b/db/migrate/20170724190805_create_poll_shifts.rb
new file mode 100644
index 000000000..8bb6eafd7
--- /dev/null
+++ b/db/migrate/20170724190805_create_poll_shifts.rb
@@ -0,0 +1,15 @@
+class CreatePollShifts < ActiveRecord::Migration
+ def change
+ create_table :poll_shifts do |t|
+ t.integer :booth_id
+ t.integer :officer_id
+ t.date :date
+
+ t.timestamps
+ end
+
+ add_index :poll_shifts, :booth_id
+ add_index :poll_shifts, :officer_id
+ add_index :poll_shifts, [:booth_id, :officer_id]
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 91a26adeb..4170c10f2 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170719174326) do
+ActiveRecord::Schema.define(version: 20170724190805) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -652,6 +652,18 @@ ActiveRecord::Schema.define(version: 20170719174326) do
add_index "poll_questions", ["proposal_id"], name: "index_poll_questions_on_proposal_id", using: :btree
add_index "poll_questions", ["tsv"], name: "index_poll_questions_on_tsv", using: :gin
+ create_table "poll_shifts", force: :cascade do |t|
+ t.integer "booth_id"
+ t.integer "officer_id"
+ t.date "date"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "poll_shifts", ["booth_id", "officer_id"], name: "index_poll_shifts_on_booth_id_and_officer_id", using: :btree
+ add_index "poll_shifts", ["booth_id"], name: "index_poll_shifts_on_booth_id", using: :btree
+ add_index "poll_shifts", ["officer_id"], name: "index_poll_shifts_on_officer_id", using: :btree
+
create_table "poll_voters", force: :cascade do |t|
t.string "document_number"
t.string "document_type"