diff --git a/app/controllers/admin/signature_sheets_controller.rb b/app/controllers/admin/signature_sheets_controller.rb
new file mode 100644
index 000000000..60299c5a6
--- /dev/null
+++ b/app/controllers/admin/signature_sheets_controller.rb
@@ -0,0 +1,32 @@
+class Admin::SignatureSheetsController < Admin::BaseController
+
+ def index
+ @signature_sheets = SignatureSheet.all
+ end
+
+ def new
+ @signature_sheet = SignatureSheet.new
+ end
+
+ def create
+ @signature_sheet = SignatureSheet.new(signature_sheet_params)
+ @signature_sheet.author = current_user
+ if @signature_sheet.save
+ @signature_sheet.delay.verify_signatures
+ redirect_to [:admin, @signature_sheet], notice: I18n.t('flash.actions.create.signature_sheet')
+ else
+ render :new
+ end
+ end
+
+ def show
+ @signature_sheet = SignatureSheet.find(params[:id])
+ end
+
+ private
+
+ def signature_sheet_params
+ params.require(:signature_sheet).permit(:signable_type, :signable_id, :document_numbers)
+ end
+
+end
\ No newline at end of file
diff --git a/app/helpers/signature_sheets_helper.rb b/app/helpers/signature_sheets_helper.rb
new file mode 100644
index 000000000..acd75a5ab
--- /dev/null
+++ b/app/helpers/signature_sheets_helper.rb
@@ -0,0 +1,8 @@
+module SignatureSheetsHelper
+
+ def signable_options
+ [[t("activerecord.models.proposal", count: 1), Proposal],
+ [t("activerecord.models.spending_proposal", count: 1), SpendingProposal]]
+ end
+
+end
\ No newline at end of file
diff --git a/app/models/signature.rb b/app/models/signature.rb
new file mode 100644
index 000000000..91bb1d816
--- /dev/null
+++ b/app/models/signature.rb
@@ -0,0 +1,84 @@
+class Signature < ActiveRecord::Base
+ belongs_to :signature_sheet
+ belongs_to :user
+
+ validates :document_number, presence: true
+ validates :signature_sheet, presence: true
+
+ scope :verified, -> { where(verified: true) }
+ scope :unverified, -> { where(verified: false) }
+
+ delegate :signable, to: :signature_sheet
+
+ def verified?
+ user_exists? || in_census?
+ end
+
+ def verify
+ if verified?
+ assign_vote
+ mark_as_verified
+ end
+ end
+
+ def assign_vote
+ if user_exists?
+ assign_vote_to_user
+ else
+ create_user
+ assign_vote_to_user
+ end
+ end
+
+ def assign_vote_to_user
+ set_user
+ signable.register_vote(user, "yes")
+ assign_signature_to_vote
+ end
+
+ def assign_signature_to_vote
+ vote = Vote.where(votable: signable, voter: user).first
+ vote.update(signature: self)
+ end
+
+ def user_exists?
+ User.where(document_number: document_number).any?
+ end
+
+ def create_user
+ user_params = {
+ document_number: document_number,
+ created_from_signature: true,
+ verified_at: Time.now,
+ erased_at: Time.now,
+ password: random_password,
+ terms_of_service: '1'
+ }
+ User.create!(user_params)
+ end
+
+ def random_password
+ (0...20).map { ('a'..'z').to_a[rand(26)] }.join
+ end
+
+ def in_census?
+ response = document_types.detect do |document_type|
+ CensusApi.new.call(document_type, document_number).valid?
+ end
+ response.present?
+ end
+
+ def set_user
+ user = User.where(document_number: document_number).first
+ update(user: user)
+ end
+
+ def mark_as_verified
+ update(verified: true)
+ end
+
+ def document_types
+ %w(1 2 3 4)
+ end
+
+end
\ No newline at end of file
diff --git a/app/models/signature_sheet.rb b/app/models/signature_sheet.rb
new file mode 100644
index 000000000..6852820bb
--- /dev/null
+++ b/app/models/signature_sheet.rb
@@ -0,0 +1,38 @@
+class SignatureSheet < ActiveRecord::Base
+ belongs_to :signable, polymorphic: true
+ belongs_to :author, class_name: 'User', foreign_key: 'author_id'
+
+ VALID_SIGNABLES = %w( Proposal SpendingProposal )
+
+ has_many :signatures
+
+ validates :author, presence: true
+ validates :signable_type, inclusion: {in: VALID_SIGNABLES}
+ validates :document_numbers, presence: true
+ validates :signable, presence: true
+ validate :signable_found
+
+ def name
+ "#{signable_name} #{signable_id}"
+ end
+
+ def signable_name
+ I18n.t("activerecord.models.#{signable_type.underscore}", count: 1)
+ end
+
+ def verify_signatures
+ parsed_document_numbers.each do |document_number|
+ signature = signatures.create(document_number: document_number)
+ signature.verify
+ end
+ update(processed: true)
+ end
+
+ def parsed_document_numbers
+ document_numbers.split(/\W+/)
+ end
+
+ def signable_found
+ errors.add(:signable_id, :not_found) if errors.messages[:signable].present?
+ end
+end
\ No newline at end of file
diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb
index 3d3e807c7..3cdb057fb 100644
--- a/app/views/admin/_menu.html.erb
+++ b/app/views/admin/_menu.html.erb
@@ -35,6 +35,14 @@
<% end %>
+ <% if feature?(:signature_sheets) %>
+
>
<%= link_to admin_banners_path do %>
<%= t("admin.menu.banner") %>
diff --git a/app/views/admin/signature_sheets/index.html.erb b/app/views/admin/signature_sheets/index.html.erb
new file mode 100644
index 000000000..74241b5b0
--- /dev/null
+++ b/app/views/admin/signature_sheets/index.html.erb
@@ -0,0 +1,31 @@
+<%= t("admin.signature_sheets.index.title") %>
+
+<%= link_to t("admin.signature_sheets.index.new"), new_admin_signature_sheet_path,
+ class: "button success float-right" %>
+
+<% if @signature_sheets.any? %>
+
+
+ | <%= t("admin.signature_sheets.name") %> |
+ <%= t("admin.signature_sheets.author") %> |
+ <%= t("admin.signature_sheets.created_at") %> |
+
+ <% @signature_sheets.each do |signature_sheet| %>
+
+ |
+ <%= link_to signature_sheet.name, [:admin, signature_sheet] %>
+ |
+
+ <%= signature_sheet.author.name %>
+ |
+
+ <%= l(signature_sheet.created_at, format: :short) %>
+ |
+
+ <% end %>
+
+<% else %>
+
+ <%= t("admin.signature_sheets.no_signature_sheets") %>
+
+<% end %>
diff --git a/app/views/admin/signature_sheets/new.html.erb b/app/views/admin/signature_sheets/new.html.erb
new file mode 100644
index 000000000..f471e1ea5
--- /dev/null
+++ b/app/views/admin/signature_sheets/new.html.erb
@@ -0,0 +1,22 @@
+<%= render 'shared/back_link' %>
+
+<%= t("admin.signature_sheets.new.title") %>
+
+<%= form_for [:admin, @signature_sheet] do |f| %>
+ <%= render 'shared/errors',
+ resource: @signature_sheet %>
+
+
+ <%= f.select :signable_type, signable_options %>
+
+
+
+ <%= f.text_field :signable_id %>
+
+
+ <%= f.label :document_numbers %>
+ <%= t("admin.signature_sheets.new.document_numbers_note") %>
+ <%= f.text_area :document_numbers, rows: "6", label: false %>
+
+ <%= f.submit(class: "button", value: t("admin.signature_sheets.new.submit")) %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/admin/signature_sheets/show.html.erb b/app/views/admin/signature_sheets/show.html.erb
new file mode 100644
index 000000000..a3787b67d
--- /dev/null
+++ b/app/views/admin/signature_sheets/show.html.erb
@@ -0,0 +1,39 @@
+<%= @signature_sheet.name %>
+
+
+ <%= t("admin.signature_sheets.show.created_at") %>
+ <%= l(@signature_sheet.created_at, format: :short) %>
+ •
+ <%= t("admin.signature_sheets.show.author") %>
+ <%= @signature_sheet.author.name %>
+
+
+
+
<%= t("admin.signature_sheets.show.documents") %>
+ <%= simple_format @signature_sheet.document_numbers %>
+
+
+
+
+
+ <%= t("admin.signature_sheets.show.verified",
+ count: @signature_sheet.signatures.verified.count ) %>
+
+
+
+
+
+
+ <%= t("admin.signature_sheets.show.unverified",
+ count: @signature_sheet.signatures.unverified.count ) %>
+ <%= t("admin.signature_sheets.show.unverified_error") %>
+
+
+ <%= @signature_sheet.signatures.unverified.map(&:document_number).join(", ") %>
+
+
+<% unless @signature_sheet.processed? %>
+
+ <%= t("admin.signature_sheets.show.loading") %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/debates/_form.html.erb b/app/views/debates/_form.html.erb
index 30152b366..c88045f26 100644
--- a/app/views/debates/_form.html.erb
+++ b/app/views/debates/_form.html.erb
@@ -1,6 +1,5 @@
<%= form_for(@debate) do |f| %>
-
<%= render 'shared/errors', resource: @debate %>
diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml
index edaaa0958..eb2e11735 100644
--- a/config/i18n-tasks.yml
+++ b/config/i18n-tasks.yml
@@ -95,6 +95,8 @@ search:
# - '{devise,simple_form}.*'
ignore_missing:
- 'unauthorized.*'
+ - 'activerecord.models.proposal'
+ - 'activerecord.models.spending_proposal'
- 'activerecord.errors.models.proposal_notification.*'
- 'activerecord.errors.models.direct_message.*'
- 'errors.messages.blank'
diff --git a/config/initializers/vote_extensions.rb b/config/initializers/vote_extensions.rb
index 345cb8f01..fce7b49eb 100644
--- a/config/initializers/vote_extensions.rb
+++ b/config/initializers/vote_extensions.rb
@@ -1,4 +1,6 @@
ActsAsVotable::Vote.class_eval do
+ belongs_to :signature
+
def self.for_debates(debates)
where(votable_type: 'Debate', votable_id: debates)
end
@@ -14,4 +16,5 @@ ActsAsVotable::Vote.class_eval do
def value
vote_flag
end
+
end
diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml
index 7de5b340e..3b4e1f1c8 100644
--- a/config/locales/activerecord.en.yml
+++ b/config/locales/activerecord.en.yml
@@ -69,6 +69,10 @@ en:
external_url: "Link to additional documentation"
geozone_id: "Scope of operation"
title: "Title"
+ signature_sheet:
+ signable_type: "Signable type"
+ signable_id: "Signable ID"
+ document_numbers: "Documents numbers"
errors:
models:
user:
@@ -90,4 +94,9 @@ en:
proposal_notification:
attributes:
minimum_interval:
- invalid: "You have to wait a minium of %{interval} days between notifications"
\ No newline at end of file
+ invalid: "You have to wait a minium of %{interval} days between notifications"
+ signature:
+ attributes:
+ document_number:
+ not_in_census: 'Not verified by Census'
+ already_voted: 'Already voted this proposal'
\ No newline at end of file
diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml
index 4dae4cb56..ea5aa14d6 100644
--- a/config/locales/activerecord.es.yml
+++ b/config/locales/activerecord.es.yml
@@ -69,6 +69,10 @@ es:
external_url: "Enlace a documentación adicional"
geozone_id: "Ámbito de actuación"
title: "Título"
+ signature_sheet:
+ signable_type: "Tipo de hoja de firmas"
+ signable_id: "ID Propuesta ciudadana/Propuesta inversión"
+ document_numbers: "Números de documentos"
errors:
models:
user:
@@ -90,4 +94,9 @@ es:
proposal_notification:
attributes:
minimum_interval:
- invalid: "Debes esperar un mínimo de %{interval} días entre notificaciones"
\ No newline at end of file
+ invalid: "Debes esperar un mínimo de %{interval} días entre notificaciones"
+ signature:
+ attributes:
+ document_number:
+ not_in_census: 'No verificado por Padrón'
+ already_voted: 'Ya ha votado esta propuesta'
\ No newline at end of file
diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml
index 370a06ccc..b442d68d5 100755
--- a/config/locales/admin.en.yml
+++ b/config/locales/admin.en.yml
@@ -111,6 +111,7 @@ en:
settings: Configuration settings
spending_proposals: Spending proposals
stats: Statistics
+ signature_sheets: Signature Sheets
moderators:
index:
title: Moderators
@@ -295,6 +296,30 @@ en:
delete:
success: Geozone successfully deleted
error: This geozone can't be deleted since there are elements attached to it
+ signature_sheets:
+ author: Author
+ created_at: Creation date
+ name: Name
+ no_signature_sheets: "There are not signature_sheets"
+ index:
+ title: Signature sheets
+ new: New signature sheets
+ new:
+ title: New signature sheets
+ document_numbers_note: "Write the numbers separated by commas (,)"
+ submit: Create signature sheet
+ show:
+ created_at: Created
+ author: Author
+ documents: Documents
+ verified:
+ one: "There is %{count} valid signature"
+ other: "There are %{count} valid signatures"
+ unverified:
+ one: "There is %{count} invalid signature"
+ other: "There are %{count} invalid signatures"
+ unverified_error: (Not verified by Census)
+ loading: "There are still signatures that are being verified by the Census, please refresh the page in a few moments"
stats:
show:
stats_title: Stats
diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml
index 8819905d8..15c6697fb 100644
--- a/config/locales/admin.es.yml
+++ b/config/locales/admin.es.yml
@@ -109,6 +109,7 @@ es:
settings: Configuración global
spending_proposals: Propuestas de inversión
stats: Estadísticas
+ signature_sheets: Hojas de firmas
moderators:
index:
title: Moderadores
@@ -293,6 +294,30 @@ es:
delete:
success: Distrito borrado correctamente
error: No se puede borrar el distrito porque ya tiene elementos asociados
+ signature_sheets:
+ author: Autor
+ created_at: Fecha de creación
+ name: Nombre
+ no_signature_sheets: "No existen hojas de firmas"
+ index:
+ title: Hojas de firmas
+ new: Nueva hoja de firmas
+ new:
+ title: Nueva hoja de firmas
+ document_numbers_note: "Introduce los números separados por comas (,)"
+ submit: Crear hoja de firmas
+ show:
+ created_at: Creado
+ author: Autor
+ documents: Documentos
+ verified:
+ one: "Hay %{count} firma válida"
+ other: "Hay %{count} firmas válidas"
+ unverified:
+ one: "Hay %{count} firma inválida"
+ other: "Hay %{count} firmas inválidas"
+ unverified_error: (No verificadas por el Padrón)
+ loading: "Aún hay firmas que se están verificando por el Padrón, por favor refresca la página en unos instantes"
stats:
show:
stats_title: Estadísticas
diff --git a/config/locales/en.yml b/config/locales/en.yml
index d006b0302..44aee4730 100755
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -154,6 +154,7 @@ en:
spending_proposal: Spending proposal
user: Account
verification/sms: phone
+ signature_sheet: Signature sheet
geozones:
none: All city
all: All scopes
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 23b2c657b..ce508a4a6 100755
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -154,6 +154,7 @@ es:
spending_proposal: la propuesta de gasto
user: la cuenta
verification/sms: el teléfono
+ signature_sheet: la hoja de firmas
geozones:
none: Toda la ciudad
all: Todos los ámbitos
diff --git a/config/locales/responders.en.yml b/config/locales/responders.en.yml
index ab1641799..f69d95c5f 100755
--- a/config/locales/responders.en.yml
+++ b/config/locales/responders.en.yml
@@ -9,7 +9,7 @@ en:
proposal: "Proposal created successfully."
proposal_notification: "Your message has been sent correctly."
spending_proposal: "Spending proposal created successfully. You can access it from %{activity}"
-
+ signature_sheet: "Signature sheet created successfully"
save_changes:
notice: Changes saved
update:
diff --git a/config/locales/responders.es.yml b/config/locales/responders.es.yml
index 387085d69..4aeb969d9 100644
--- a/config/locales/responders.es.yml
+++ b/config/locales/responders.es.yml
@@ -9,6 +9,7 @@ es:
proposal: "Propuesta creada correctamente."
proposal_notification: "Tu message ha sido enviado correctamente."
spending_proposal: "Propuesta de inversión creada correctamente. Puedes acceder a ella desde %{activity}"
+ signature_sheet: "Hoja de firmas creada correctamente"
save_changes:
notice: Cambios guardados
update:
diff --git a/config/locales/settings.en.yml b/config/locales/settings.en.yml
index b21f32cd3..20aa31c3d 100755
--- a/config/locales/settings.en.yml
+++ b/config/locales/settings.en.yml
@@ -27,6 +27,7 @@ en:
facebook_login: Facebook login
google_login: Google login
debates: Debates
+ signature_sheets: Signature sheets
spending_proposals: Investment projects
spending_proposal_features:
voting_allowed: Voting on investment projects
diff --git a/config/locales/settings.es.yml b/config/locales/settings.es.yml
index a3171dfe7..f01235528 100644
--- a/config/locales/settings.es.yml
+++ b/config/locales/settings.es.yml
@@ -27,6 +27,7 @@ es:
facebook_login: Registro con Facebook
google_login: Registro con Google
debates: Debates
+ signature_sheets: Hojas de firmas
spending_proposals: Propuestas de inversión
spending_proposal_features:
voting_allowed: Votaciones sobre propuestas de inversión.
diff --git a/config/routes.rb b/config/routes.rb
index 885d155db..5019cda40 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -149,6 +149,8 @@ Rails.application.routes.draw do
get :summary, on: :collection
end
+ resources :signature_sheets, only: [:index, :new, :create, :show]
+
resources :banners, only: [:index, :new, :create, :edit, :update, :destroy] do
collection { get :search}
end
diff --git a/db/dev_seeds.rb b/db/dev_seeds.rb
index f0d710f2d..dfdd42731 100644
--- a/db/dev_seeds.rb
+++ b/db/dev_seeds.rb
@@ -30,11 +30,13 @@ Setting.create(key: 'feature.spending_proposal_features.voting_allowed', value:
Setting.create(key: 'feature.twitter_login', value: "true")
Setting.create(key: 'feature.facebook_login', value: "true")
Setting.create(key: 'feature.google_login', value: "true")
+Setting.create(key: 'feature.signature_sheets', value: "true")
Setting.create(key: 'per_page_code', value: "")
Setting.create(key: 'comments_body_max_length', value: '1000')
Setting.create(key: 'mailer_from_name', value: 'Consul')
Setting.create(key: 'mailer_from_address', value: 'noreply@consul.dev')
+
puts "Creating Geozones"
('A'..'Z').each { |i| Geozone.create(name: "District #{i}", external_code: i.ord, census_code: i.ord) }
diff --git a/db/migrate/20161214212918_create_signature_sheets.rb b/db/migrate/20161214212918_create_signature_sheets.rb
new file mode 100644
index 000000000..d34728a34
--- /dev/null
+++ b/db/migrate/20161214212918_create_signature_sheets.rb
@@ -0,0 +1,11 @@
+class CreateSignatureSheets < ActiveRecord::Migration
+ def change
+ create_table :signature_sheets do |t|
+ t.references :signable, polymorphic: true
+ t.text :document_numbers
+ t.boolean :processed, default: false
+ t.references :author
+ t.timestamps
+ end
+ end
+end
\ No newline at end of file
diff --git a/db/migrate/20161214233817_create_signatures.rb b/db/migrate/20161214233817_create_signatures.rb
new file mode 100644
index 000000000..fd6b53d3a
--- /dev/null
+++ b/db/migrate/20161214233817_create_signatures.rb
@@ -0,0 +1,11 @@
+class CreateSignatures < ActiveRecord::Migration
+ def change
+ create_table :signatures do |t|
+ t.references :signature_sheet
+ t.references :user
+ t.string :document_number
+ t.boolean :verified, default: false
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20161221131403_add_signture_id_to_votes.rb b/db/migrate/20161221131403_add_signture_id_to_votes.rb
new file mode 100644
index 000000000..28851fdac
--- /dev/null
+++ b/db/migrate/20161221131403_add_signture_id_to_votes.rb
@@ -0,0 +1,5 @@
+class AddSigntureIdToVotes < ActiveRecord::Migration
+ def change
+ add_reference :votes, :signature, index: true
+ end
+end
diff --git a/db/migrate/20161221151239_add_created_from_signature_to_users.rb b/db/migrate/20161221151239_add_created_from_signature_to_users.rb
new file mode 100644
index 000000000..214bd43cb
--- /dev/null
+++ b/db/migrate/20161221151239_add_created_from_signature_to_users.rb
@@ -0,0 +1,5 @@
+class AddCreatedFromSignatureToUsers < ActiveRecord::Migration
+ def change
+ add_column :users, :created_from_signature, :boolean, default: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a93942873..acfc87dbb 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: 20161102133838) do
+ActiveRecord::Schema.define(version: 20161221151239) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -123,10 +123,10 @@ ActiveRecord::Schema.define(version: 20161102133838) do
t.string "visit_id"
t.datetime "hidden_at"
t.integer "flags_count", default: 0
+ t.datetime "ignored_flag_at"
t.integer "cached_votes_total", default: 0
t.integer "cached_votes_up", default: 0
t.integer "cached_votes_down", default: 0
- t.datetime "ignored_flag_at"
t.integer "comments_count", default: 0
t.datetime "confirmed_hide_at"
t.integer "cached_anonymous_votes_total", default: 0
@@ -145,7 +145,6 @@ ActiveRecord::Schema.define(version: 20161102133838) do
add_index "debates", ["cached_votes_total"], name: "index_debates_on_cached_votes_total", using: :btree
add_index "debates", ["cached_votes_up"], name: "index_debates_on_cached_votes_up", using: :btree
add_index "debates", ["confidence_score"], name: "index_debates_on_confidence_score", using: :btree
- add_index "debates", ["description"], name: "index_debates_on_description", using: :btree
add_index "debates", ["geozone_id"], name: "index_debates_on_geozone_id", using: :btree
add_index "debates", ["hidden_at"], name: "index_debates_on_hidden_at", using: :btree
add_index "debates", ["hot_score"], name: "index_debates_on_hot_score", using: :btree
@@ -310,7 +309,6 @@ ActiveRecord::Schema.define(version: 20161102133838) do
add_index "proposals", ["author_id"], name: "index_proposals_on_author_id", using: :btree
add_index "proposals", ["cached_votes_up"], name: "index_proposals_on_cached_votes_up", using: :btree
add_index "proposals", ["confidence_score"], name: "index_proposals_on_confidence_score", using: :btree
- add_index "proposals", ["description"], name: "index_proposals_on_description", using: :btree
add_index "proposals", ["geozone_id"], name: "index_proposals_on_geozone_id", using: :btree
add_index "proposals", ["hidden_at"], name: "index_proposals_on_hidden_at", using: :btree
add_index "proposals", ["hot_score"], name: "index_proposals_on_hot_score", using: :btree
@@ -326,6 +324,25 @@ ActiveRecord::Schema.define(version: 20161102133838) do
add_index "settings", ["key"], name: "index_settings_on_key", using: :btree
+ create_table "signature_sheets", force: :cascade do |t|
+ t.integer "signable_id"
+ t.string "signable_type"
+ t.text "document_numbers"
+ t.boolean "processed", default: false
+ t.integer "author_id"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ create_table "signatures", force: :cascade do |t|
+ t.integer "signature_sheet_id"
+ t.integer "user_id"
+ t.string "document_number"
+ t.boolean "verified", default: false
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
create_table "spending_proposals", force: :cascade do |t|
t.string "title"
t.text "description"
@@ -464,7 +481,8 @@ ActiveRecord::Schema.define(version: 20161102133838) do
t.boolean "email_digest", default: true
t.boolean "email_on_direct_message", default: true
t.boolean "official_position_badge", default: false
- t.datetime "password_changed_at", default: '2016-11-02 13:51:14', null: false
+ t.datetime "password_changed_at", default: '2016-12-21 17:55:08', null: false
+ t.boolean "created_from_signature", default: false
end
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
@@ -544,8 +562,10 @@ ActiveRecord::Schema.define(version: 20161102133838) do
t.integer "vote_weight"
t.datetime "created_at"
t.datetime "updated_at"
+ t.integer "signature_id"
end
+ add_index "votes", ["signature_id"], name: "index_votes_on_signature_id", using: :btree
add_index "votes", ["votable_id", "votable_type", "vote_scope"], name: "index_votes_on_votable_id_and_votable_type_and_vote_scope", using: :btree
add_index "votes", ["voter_id", "voter_type", "vote_scope"], name: "index_votes_on_voter_id_and_voter_type_and_vote_scope", using: :btree
diff --git a/db/seeds.rb b/db/seeds.rb
index 972a0a495..d8be294c7 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -64,6 +64,7 @@ Setting['feature.twitter_login'] = true
Setting['feature.facebook_login'] = true
Setting['feature.google_login'] = true
Setting['feature.public_stats'] = true
+Setting['feature.signature_sheets'] = true
# Spending proposals feature flags
Setting['feature.spending_proposal_features.voting_allowed'] = true
diff --git a/lib/census_api.rb b/lib/census_api.rb
index 678a748c6..7f88557f2 100644
--- a/lib/census_api.rb
+++ b/lib/census_api.rb
@@ -74,7 +74,7 @@ class CensusApi
if end_point_available?
client.call(:get_habita_datos, message: request(document_type, document_number)).body
else
- stubbed_response_body
+ stubbed_response(document_type, document_number)
end
end
@@ -97,8 +97,20 @@ class CensusApi
Rails.env.staging? || Rails.env.preproduction? || Rails.env.production?
end
- def stubbed_response_body
- {get_habita_datos_response: {get_habita_datos_return: {hay_errores: false, datos_habitante: { item: {fecha_nacimiento_string: "31-12-1980", identificador_documento: "12345678Z", descripcion_sexo: "Varón" }}, datos_vivienda: {item: {codigo_postal: "28013", codigo_distrito: "01"}}}}}
+ def stubbed_response(document_type, document_number)
+ if document_number == "12345678Z" && document_type == "1"
+ stubbed_valid_response
+ else
+ stubbed_invalid_response
+ end
+ end
+
+ def stubbed_valid_response
+ {get_habita_datos_response: {get_habita_datos_return: {datos_habitante: { item: {fecha_nacimiento_string: "31-12-1980", identificador_documento: "12345678Z", descripcion_sexo: "Varón", nombre: "José", apellido1: "García" }}, datos_vivienda: {item: {codigo_postal: "28013", codigo_distrito: "01"}}}}}
+ end
+
+ def stubbed_invalid_response
+ {get_habita_datos_response: {get_habita_datos_return: {datos_habitante: {}, datos_vivienda: {}}}}
end
def is_dni?(document_type)
diff --git a/spec/factories.rb b/spec/factories.rb
index c53f16be6..322f214d5 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -343,4 +343,15 @@ FactoryGirl.define do
association :sender, factory: :user
association :receiver, factory: :user
end
+
+ factory :signature_sheet do
+ association :signable, factory: :proposal
+ association :author, factory: :user
+ document_numbers "123A, 456B, 789C"
+ end
+
+ factory :signature do
+ signature_sheet
+ sequence(:document_number) { |n| "#{n}A" }
+ end
end
diff --git a/spec/features/admin/signature_sheets_spec.rb b/spec/features/admin/signature_sheets_spec.rb
new file mode 100644
index 000000000..a2474834a
--- /dev/null
+++ b/spec/features/admin/signature_sheets_spec.rb
@@ -0,0 +1,71 @@
+require 'rails_helper'
+
+feature 'Signature sheets' do
+
+ background do
+ admin = create(:administrator)
+ login_as(admin.user)
+ end
+
+ scenario "Index" do
+ 3.times { create(:signature_sheet) }
+
+ visit admin_signature_sheets_path
+
+ expect(page).to have_css(".signature_sheet", count: 3)
+
+ SignatureSheet.all.each do |signature_sheet|
+ expect(page).to have_content signature_sheet.name
+ end
+ end
+
+ scenario 'Create' do
+ proposal = create(:proposal)
+ visit new_admin_signature_sheet_path
+
+ select "Citizen proposal", from: "signature_sheet_signable_type"
+ fill_in "signature_sheet_signable_id", with: proposal.id
+ fill_in "signature_sheet_document_numbers", with: "12345678Z, 99999999Z"
+ click_button "Create signature sheet"
+
+ expect(page).to have_content "Signature sheet created successfully"
+
+ visit proposal_path(proposal)
+
+ expect(page).to have_content "1 support"
+ end
+
+ scenario 'Errors on create' do
+ visit new_admin_signature_sheet_path
+
+ click_button "Create signature sheet"
+
+ expect(page).to have_content error_message
+ end
+
+ scenario 'Show' do
+ proposal = create(:proposal)
+ user = Administrator.first.user
+ signature_sheet = create(:signature_sheet,
+ signable: proposal,
+ document_numbers: "12345678Z, 123A, 123B",
+ author: user)
+ signature_sheet.verify_signatures
+
+ visit admin_signature_sheet_path(signature_sheet)
+
+ expect(page).to have_content "Citizen proposal #{proposal.id}"
+ expect(page).to have_content "12345678Z, 123A, 123B"
+ expect(page).to have_content signature_sheet.created_at.strftime("%d %b %H:%M")
+ expect(page).to have_content user.name
+
+ within("#verified_signatures") do
+ expect(page).to have_content 1
+ end
+
+ within("#unverified_signatures") do
+ expect(page).to have_content 2
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/spec/features/management/document_verifications_spec.rb b/spec/features/management/document_verifications_spec.rb
index 674349815..130630762 100644
--- a/spec/features/management/document_verifications_spec.rb
+++ b/spec/features/management/document_verifications_spec.rb
@@ -47,7 +47,7 @@ feature 'DocumentVerifications' do
scenario 'Verifying a user which does exists in the census but not in the db redirects allows sending an email' do
visit management_document_verifications_path
- fill_in 'document_verification_document_number', with: '1234'
+ fill_in 'document_verification_document_number', with: '12345678Z'
click_button 'Check'
expect(page).to have_content "Please introduce the email used on the account"
@@ -66,7 +66,7 @@ feature 'DocumentVerifications' do
expect_any_instance_of(Verification::Management::Document).to receive(:under_sixteen?).and_return(true)
visit management_document_verifications_path
- fill_in 'document_verification_document_number', with: '1234'
+ fill_in 'document_verification_document_number', with: '12345678Z'
click_button 'Check'
expect(page).to have_content "You must be over 16 to verify your account."
diff --git a/spec/features/management/email_verifications_spec.rb b/spec/features/management/email_verifications_spec.rb
index 22987f317..d68d9bd5f 100644
--- a/spec/features/management/email_verifications_spec.rb
+++ b/spec/features/management/email_verifications_spec.rb
@@ -8,7 +8,7 @@ feature 'EmailVerifications' do
user = create(:user)
visit management_document_verifications_path
- fill_in 'document_verification_document_number', with: '1234'
+ fill_in 'document_verification_document_number', with: '12345678Z'
click_button 'Check'
expect(page).to have_content "Please introduce the email used on the account"
@@ -30,7 +30,7 @@ feature 'EmailVerifications' do
expect(page).to_not have_link "Verify my account"
expect(page).to have_content "Account verified"
- expect(user.reload.document_number).to eq('1234')
+ expect(user.reload.document_number).to eq('12345678Z')
expect(user).to be_level_three_verified
end
diff --git a/spec/features/management/managed_users_spec.rb b/spec/features/management/managed_users_spec.rb
index 78010bc39..311b8f5cd 100644
--- a/spec/features/management/managed_users_spec.rb
+++ b/spec/features/management/managed_users_spec.rb
@@ -57,7 +57,7 @@ feature 'Managed User' do
user = create(:user)
visit management_document_verifications_path
- fill_in 'document_verification_document_number', with: '1234'
+ fill_in 'document_verification_document_number', with: '12345678Z'
click_button 'Check'
within(".account-info") do
@@ -66,7 +66,7 @@ feature 'Managed User' do
expect(page).not_to have_content "Email"
expect(page).to have_content "Document type"
expect(page).to have_content "Document number"
- expect(page).to have_content "1234"
+ expect(page).to have_content "12345678Z"
end
expect(page).to have_content "Please introduce the email used on the account"
@@ -88,7 +88,7 @@ feature 'Managed User' do
login_as_manager
visit management_document_verifications_path
- fill_in 'document_verification_document_number', with: '1234'
+ fill_in 'document_verification_document_number', with: '12345678Z'
click_button 'Check'
expect(page).to have_content "Please introduce the email used on the account"
diff --git a/spec/features/management/users_spec.rb b/spec/features/management/users_spec.rb
index 1a0618e60..694720cc0 100644
--- a/spec/features/management/users_spec.rb
+++ b/spec/features/management/users_spec.rb
@@ -9,7 +9,7 @@ feature 'Users' do
scenario 'Create a level 3 user from scratch' do
visit management_document_verifications_path
- fill_in 'document_verification_document_number', with: '1234'
+ fill_in 'document_verification_document_number', with: '12345678Z'
click_button 'Check'
expect(page).to have_content "Please introduce the email used on the account"
@@ -45,10 +45,10 @@ feature 'Users' do
end
scenario 'Delete a level 2 user account from document verification page', :js do
- level_2_user = create(:user, :level_two, document_number: 13579)
+ level_2_user = create(:user, :level_two, document_number: "12345678Z")
visit management_document_verifications_path
- fill_in 'document_verification_document_number', with: '13579'
+ fill_in 'document_verification_document_number', with: '12345678Z'
click_button 'Check'
expect(page).to_not have_content "This user account is already verified."
@@ -62,7 +62,7 @@ feature 'Users' do
expect(level_2_user.reload.erase_reason).to eq "Deleted by manager: manager_user_#{Manager.last.user_id}"
visit management_document_verifications_path
- fill_in 'document_verification_document_number', with: '13579'
+ fill_in 'document_verification_document_number', with: '12345678Z'
click_button 'Check'
expect(page).to have_content "no user account associated to it"
diff --git a/spec/models/signature_sheet_spec.rb b/spec/models/signature_sheet_spec.rb
new file mode 100644
index 000000000..d8f5c3513
--- /dev/null
+++ b/spec/models/signature_sheet_spec.rb
@@ -0,0 +1,81 @@
+require 'rails_helper'
+
+describe SignatureSheet do
+
+ let(:signature_sheet) { build(:signature_sheet) }
+
+ describe "validations" do
+
+ it "should be valid" do
+ expect(signature_sheet).to be_valid
+ end
+
+ it "should be valid with a valid signable" do
+ signature_sheet.signable = create(:proposal)
+ expect(signature_sheet).to be_valid
+
+ signature_sheet.signable = create(:spending_proposal)
+ expect(signature_sheet).to be_valid
+ end
+
+ it "should not be valid without signable" do
+ signature_sheet.signable = nil
+ expect(signature_sheet).to_not be_valid
+ end
+
+ it "should not be valid without a valid signable" do
+ signature_sheet.signable = create(:comment)
+ expect(signature_sheet).to_not be_valid
+ end
+
+ it "should not be valid without document numbers" do
+ signature_sheet.document_numbers = nil
+ expect(signature_sheet).to_not be_valid
+ end
+
+ it "should not be valid without an author" do
+ signature_sheet.author = nil
+ expect(signature_sheet).to_not be_valid
+ end
+ end
+
+ describe "#name" do
+ it "returns name for proposal signature sheets" do
+ proposal = create(:proposal)
+ signature_sheet.signable = proposal
+
+ expect(signature_sheet.name).to eq("Citizen proposal #{proposal.id}")
+ end
+ it "returns name for spending proposal signature sheets" do
+ spending_proposal = create(:spending_proposal)
+ signature_sheet.signable = spending_proposal
+
+ expect(signature_sheet.name).to eq("Spending proposal #{spending_proposal.id}")
+ end
+ end
+
+ describe "#verify_signatures" do
+ it "creates signatures for each document number" do
+ signature_sheet = create(:signature_sheet, document_numbers: "123A, 456B")
+ signature_sheet.verify_signatures
+
+ expect(Signature.count).to eq(2)
+ end
+
+ it "marks signature sheet as processed" do
+ signature_sheet = create(:signature_sheet)
+ signature_sheet.verify_signatures
+
+ expect(signature_sheet.processed).to eq(true)
+ end
+ end
+
+ describe "#parsed_document_numbers" do
+ it "returns an array after spliting document numbers by newlines or commas" do
+ signature_sheet.document_numbers = "123A\r\n456B\n789C,123B"
+
+ expect(signature_sheet.parsed_document_numbers).to eq(['123A', '456B', '789C', '123B'])
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/spec/models/signature_spec.rb b/spec/models/signature_spec.rb
new file mode 100644
index 000000000..6f7237484
--- /dev/null
+++ b/spec/models/signature_spec.rb
@@ -0,0 +1,171 @@
+require 'rails_helper'
+
+describe Signature do
+
+ let(:signature) { build(:signature) }
+
+ describe "validations" do
+
+ it "should be valid" do
+ expect(signature).to be_valid
+ end
+
+ it "should not be valid without a document number" do
+ signature.document_number = nil
+ expect(signature).to_not be_valid
+ end
+
+ it "should not be valid without an associated signature sheet" do
+ signature.signature_sheet = nil
+ expect(signature).to_not be_valid
+ end
+
+ end
+
+ describe "#verified?" do
+
+ it "returns true if user exists" do
+ user = create(:user, :level_two, document_number: "123A")
+ signature = create(:signature, document_number: user.document_number)
+
+ expect(signature.verified?).to eq(true)
+ end
+
+ it "returns true if document number in census" do
+ signature = create(:signature, document_number: "12345678Z")
+
+ expect(signature.verified?).to eq(true)
+ end
+
+ it "returns false if user does not exist and not in census" do
+ signature = create(:signature, document_number: "123A")
+
+ expect(signature.verified?).to eq(false)
+ end
+
+ end
+
+ describe "#assign_vote" do
+
+ describe "existing user" do
+
+ it "assigns vote to user" do
+ user = create(:user, :level_two, document_number: "123A")
+ signature = create(:signature, document_number: user.document_number)
+ proposal = signature.signable
+
+ signature.assign_vote
+
+ expect(user.voted_for?(proposal)).to be
+ end
+
+ it "does not assign vote to user multiple times" do
+ user = create(:user, :level_two, document_number: "123A")
+ signature = create(:signature, document_number: user.document_number)
+
+ signature.assign_vote
+ signature.assign_vote
+
+ expect(Vote.count).to eq(1)
+ end
+
+ it "does not assign vote to user if already voted" do
+ proposal = create(:proposal)
+ user = create(:user, :level_two, document_number: "123A")
+ vote = create(:vote, votable: proposal, voter: user)
+ signature_sheet = create(:signature_sheet, signable: proposal)
+ signature = create(:signature, signature_sheet: signature_sheet, document_number: user.document_number)
+
+ signature.assign_vote
+
+ expect(Vote.count).to eq(1)
+ end
+
+ it "marks the vote as coming from a signature" do
+ signature = create(:signature, document_number: "12345678Z")
+
+ signature.assign_vote
+
+ expect(Vote.last.signature).to eq(signature)
+ end
+
+ end
+
+ describe "inexistent user" do
+
+ it "creates a user with that document number" do
+ signature = create(:signature, document_number: "12345678Z")
+ proposal = signature.signable
+
+ signature.assign_vote
+
+ user = User.last
+ expect(user.document_number).to eq("12345678Z")
+ expect(user.created_from_signature).to eq(true)
+ expect(user.verified_at).to be
+ expect(user.erased_at).to be
+ end
+
+ it "assign the vote to newly created user" do
+ signature = create(:signature, document_number: "12345678Z")
+ proposal = signature.signable
+
+ signature.assign_vote
+
+ user = signature.user
+ expect(user.voted_for?(proposal)).to be
+ end
+
+ it "assigns signature to vote" do
+ signature = create(:signature, document_number: "12345678Z")
+
+ signature.assign_vote
+
+ expect(Vote.last.signature).to eq(signature)
+ end
+ end
+
+ end
+
+ describe "#verify" do
+
+ describe "document in census" do
+
+ it "calls assign_vote" do
+ signature = create(:signature, document_number: "12345678Z")
+
+ expect(signature).to receive(:assign_vote)
+ signature.verify
+ end
+
+ it "sets signature as verified" do
+ user = create(:user, :level_two, document_number: "123A")
+ signature = create(:signature, document_number: user.document_number)
+
+ signature.verify
+
+ expect(signature).to be_verified
+ end
+
+ end
+
+ describe "document not in census" do
+
+ it "does not call assign_vote" do
+ signature = create(:signature, document_number: "123A")
+
+ expect(signature).to_not receive(:assign_vote)
+ signature.verify
+ end
+
+ it "maintains signature as not verified" do
+ signature = create(:signature, document_number: "123A")
+
+ signature.verify
+ expect(signature).to_not be_verified
+ end
+ end
+
+ end
+
+end
\ No newline at end of file