Verify poll ballots
This commit is contained in:
@@ -2,6 +2,7 @@ class Budget
|
|||||||
class Ballot < ActiveRecord::Base
|
class Ballot < ActiveRecord::Base
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :budget
|
belongs_to :budget
|
||||||
|
belongs_to :poll_ballot, class_name: "Poll::Ballot"
|
||||||
|
|
||||||
has_many :lines, dependent: :destroy
|
has_many :lines, dependent: :destroy
|
||||||
has_many :investments, through: :lines
|
has_many :investments, through: :lines
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class Budget
|
|||||||
end
|
end
|
||||||
|
|
||||||
def store_user_heading
|
def store_user_heading
|
||||||
ballot.user.update(balloted_heading_id: heading.id)
|
ballot.user.update(balloted_heading_id: heading.id) unless ballot.physical == true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
36
app/models/poll/ballot.rb
Normal file
36
app/models/poll/ballot.rb
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
class Poll::Ballot < ActiveRecord::Base
|
||||||
|
belongs_to :ballot_sheet, class_name: Poll::BallotSheet
|
||||||
|
|
||||||
|
validates :ballot_sheet_id, presence: true
|
||||||
|
|
||||||
|
def verify
|
||||||
|
investments.each do |investment_id|
|
||||||
|
add_investment(investment_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_investment(investment_id)
|
||||||
|
investment = find_investment(investment_id)
|
||||||
|
|
||||||
|
if investment.present? && not_already_added?(investment)
|
||||||
|
ballot.add_investment(investment)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def investments
|
||||||
|
data.split(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
def ballot
|
||||||
|
Budget::Ballot.where(poll_ballot: self).first
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_investment(investment_id)
|
||||||
|
ballot.budget.investments.where(id: investment_id).first
|
||||||
|
end
|
||||||
|
|
||||||
|
def not_already_added?(investment)
|
||||||
|
ballot.lines.where(investment: investment).blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
class Poll::BallotSheet < ActiveRecord::Base
|
class Poll::BallotSheet < ActiveRecord::Base
|
||||||
belongs_to :poll
|
belongs_to :poll
|
||||||
belongs_to :officer_assignment
|
belongs_to :officer_assignment
|
||||||
|
has_many :ballots, class_name: Poll::Ballot
|
||||||
|
|
||||||
validates :data, presence: true
|
validates :data, presence: true
|
||||||
validates :poll_id, presence: true
|
validates :poll_id, presence: true
|
||||||
@@ -9,4 +10,33 @@ class Poll::BallotSheet < ActiveRecord::Base
|
|||||||
def author
|
def author
|
||||||
officer_assignment.officer.name
|
officer_assignment.officer.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def verify_ballots
|
||||||
|
parsed_ballots.each_with_index do |investment_ids, index|
|
||||||
|
ballot = create_ballots(investment_ids, index)
|
||||||
|
ballot.verify
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parsed_ballots
|
||||||
|
data.split(/[;\n]/)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_ballots(investment_ids, index)
|
||||||
|
poll_ballot = Poll::Ballot.where(ballot_sheet: self,
|
||||||
|
data: investment_ids,
|
||||||
|
external_id: index).first_or_create
|
||||||
|
create_ballot(poll_ballot)
|
||||||
|
poll_ballot
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_ballot(poll_ballot)
|
||||||
|
Budget::Ballot.where(physical: true,
|
||||||
|
user: nil,
|
||||||
|
poll_ballot: poll_ballot,
|
||||||
|
budget: poll.budget).first_or_create
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
10
db/migrate/20180704093831_create_poll_ballot.rb
Normal file
10
db/migrate/20180704093831_create_poll_ballot.rb
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
class CreatePollBallot < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :poll_ballots do |t|
|
||||||
|
t.integer :ballot_sheet_id
|
||||||
|
t.text :data
|
||||||
|
t.integer :external_id
|
||||||
|
t.timestamps null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
class AddPhysicalToBudgetBallot < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :budget_ballots, :physical, :boolean, default: false
|
||||||
|
add_column :budget_ballots, :poll_ballot_id, :integer
|
||||||
|
end
|
||||||
|
end
|
||||||
14
db/schema.rb
14
db/schema.rb
@@ -140,8 +140,10 @@ ActiveRecord::Schema.define(version: 20190205131722) do
|
|||||||
create_table "budget_ballots", force: :cascade do |t|
|
create_table "budget_ballots", force: :cascade do |t|
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.integer "budget_id"
|
t.integer "budget_id"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
|
t.boolean "physical", default: false
|
||||||
|
t.integer "poll_ballot_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "budget_content_blocks", force: :cascade do |t|
|
create_table "budget_content_blocks", force: :cascade do |t|
|
||||||
@@ -938,6 +940,14 @@ ActiveRecord::Schema.define(version: 20190205131722) do
|
|||||||
add_index "poll_ballot_sheets", ["officer_assignment_id"], name: "index_poll_ballot_sheets_on_officer_assignment_id", using: :btree
|
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
|
add_index "poll_ballot_sheets", ["poll_id"], name: "index_poll_ballot_sheets_on_poll_id", using: :btree
|
||||||
|
|
||||||
|
create_table "poll_ballots", force: :cascade do |t|
|
||||||
|
t.integer "ballot_sheet_id"
|
||||||
|
t.text "data"
|
||||||
|
t.integer "external_id"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
end
|
||||||
|
|
||||||
create_table "poll_booth_assignments", force: :cascade do |t|
|
create_table "poll_booth_assignments", force: :cascade do |t|
|
||||||
t.integer "booth_id"
|
t.integer "booth_id"
|
||||||
t.integer "poll_id"
|
t.integer "poll_id"
|
||||||
|
|||||||
@@ -153,6 +153,11 @@ FactoryBot.define do
|
|||||||
data "1234;9876;5678\n1000;2000;3000;9999"
|
data "1234;9876;5678\n1000;2000;3000;9999"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :poll_ballot, class: "Poll::Ballot" do
|
||||||
|
association :ballot_sheet, factory: :poll_ballot_sheet
|
||||||
|
data "1,2,3"
|
||||||
|
end
|
||||||
|
|
||||||
factory :officing_residence, class: "Officing::Residence" do
|
factory :officing_residence, class: "Officing::Residence" do
|
||||||
user
|
user
|
||||||
association :officer, factory: :poll_officer
|
association :officer, factory: :poll_officer
|
||||||
|
|||||||
@@ -37,4 +37,24 @@ describe Poll::BallotSheet do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#verify_ballots" do
|
||||||
|
it "creates ballots for each document number" do
|
||||||
|
budget = create(:budget)
|
||||||
|
poll = create(:poll, budget: budget)
|
||||||
|
poll_ballot = create(:poll_ballot_sheet, poll: poll, data: "1,2,3;4,5,6")
|
||||||
|
poll_ballot.verify_ballots
|
||||||
|
|
||||||
|
expect(Poll::Ballot.count).to eq(2)
|
||||||
|
expect(Budget::Ballot.count).to eq(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#parsed_ballots" do
|
||||||
|
it "splits ballots by ';' or '\n'" do
|
||||||
|
data = "1,2,3;4,5,6\n7,8,9"
|
||||||
|
ballot_sheet.update(data: data)
|
||||||
|
|
||||||
|
expect(ballot_sheet.parsed_ballots).to eq(["1,2,3", "4,5,6", "7,8,9"])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
136
spec/models/poll/ballot_spec.rb
Normal file
136
spec/models/poll/ballot_spec.rb
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe Poll::Ballot do
|
||||||
|
|
||||||
|
let(:budget){ create(:budget) }
|
||||||
|
let(:group){ create(:budget_group, budget: budget) }
|
||||||
|
let(:heading){ create(:budget_heading, group: group, price: 10000000) }
|
||||||
|
let(:investment){ create(:budget_investment, :selected, price: 5000000, heading: heading) }
|
||||||
|
let(:poll) { create(:poll, budget: budget) }
|
||||||
|
let(:poll_ballot_sheet) { create(:poll_ballot_sheet, poll: poll) }
|
||||||
|
let(:poll_ballot) { create(:poll_ballot, ballot_sheet: poll_ballot_sheet, external_id: 1, data: investment.id) }
|
||||||
|
let!(:ballot) { create(:budget_ballot, budget: budget, physical: true, poll_ballot: poll_ballot) }
|
||||||
|
|
||||||
|
describe "#verify" do
|
||||||
|
|
||||||
|
it "adds ballot lines until there are sufficiente funds" do
|
||||||
|
investment2 = create(:budget_investment, :selected, price: 2000000, heading: heading)
|
||||||
|
investment3 = create(:budget_investment, :selected, price: 2000000, heading: heading)
|
||||||
|
investment4 = create(:budget_investment, :selected, price: 2000000, heading: heading)
|
||||||
|
|
||||||
|
poll_ballot.update(data: [investment.id, investment2.id, investment3.id, investment4.id].join(","))
|
||||||
|
poll_ballot.verify
|
||||||
|
|
||||||
|
expect(poll_ballot.ballot.lines.count).to eq(3)
|
||||||
|
expect(poll_ballot.ballot.lines.pluck(:investment_id).sort).to eq([investment.id, investment2.id, investment3.id].sort)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "adds ballot lines if they are from valid headings" do
|
||||||
|
other_heading = create(:budget_heading, group: group, price: 10000000)
|
||||||
|
|
||||||
|
investment2 = create(:budget_investment, :selected, price: 2000000, heading: heading)
|
||||||
|
investment3 = create(:budget_investment, :selected, price: 2000000, heading: heading)
|
||||||
|
investment4 = create(:budget_investment, :selected, price: 2000000, heading: other_heading)
|
||||||
|
|
||||||
|
poll_ballot.update(data: [investment.id, investment2.id, investment3.id, investment4.id].join(","))
|
||||||
|
poll_ballot.verify
|
||||||
|
|
||||||
|
expect(poll_ballot.ballot.lines.count).to eq(3)
|
||||||
|
expect(poll_ballot.ballot.lines.pluck(:investment_id).sort).to eq([investment.id, investment2.id, investment3.id].sort)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "adds ballot lines if they are from selectable" do
|
||||||
|
investment2 = create(:budget_investment, :selected, price: 2000000, heading: heading)
|
||||||
|
investment3 = create(:budget_investment, :selected, price: 2000000, heading: heading)
|
||||||
|
investment4 = create(:budget_investment, price: 2000000, heading: heading)
|
||||||
|
|
||||||
|
poll_ballot.update(data: [investment.id, investment2.id, investment3.id, investment4.id].join(","))
|
||||||
|
poll_ballot.verify
|
||||||
|
|
||||||
|
expect(poll_ballot.ballot.lines.count).to eq(3)
|
||||||
|
expect(poll_ballot.ballot.lines.pluck(:investment_id).sort).to eq([investment.id, investment2.id, investment3.id].sort)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#add_investment" do
|
||||||
|
|
||||||
|
describe "Money" do
|
||||||
|
it "is not valid if insufficient funds" do
|
||||||
|
investment.update(price: heading.price + 1)
|
||||||
|
expect(poll_ballot.add_investment(investment.id)).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if sufficient funds" do
|
||||||
|
investment.update(price: heading.price - 1)
|
||||||
|
expect(poll_ballot.add_investment(investment.id)).to be(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Heading" do
|
||||||
|
it "is not valid if investment heading is not valid" do
|
||||||
|
expect(poll_ballot.add_investment(investment.id)).to be(true)
|
||||||
|
|
||||||
|
other_heading = create(:budget_heading, group: group, price: 10000000)
|
||||||
|
other_investment = create(:budget_investment, :selected, price: 1000000, heading: other_heading)
|
||||||
|
|
||||||
|
expect(poll_ballot.add_investment(other_investment.id)).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if investment heading is valid" do
|
||||||
|
expect(poll_ballot.add_investment(investment.id)).to be(true)
|
||||||
|
|
||||||
|
other_investment = create(:budget_investment, :selected, price: 1000000, heading: heading)
|
||||||
|
|
||||||
|
expect(poll_ballot.add_investment(other_investment.id)).to be(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Selectibility" do
|
||||||
|
it "is not valid if investment is unselected" do
|
||||||
|
investment.update(selected: false)
|
||||||
|
expect(poll_ballot.add_investment(investment.id)).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if investment is selected" do
|
||||||
|
investment.update(selected: true, price: 20000)
|
||||||
|
expect(poll_ballot.add_investment(investment.id)).to be(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Budget" do
|
||||||
|
it "is not valid if investment belongs to a different budget" do
|
||||||
|
other_budget = create(:budget)
|
||||||
|
investment.update(budget: other_budget)
|
||||||
|
expect(poll_ballot.add_investment(investment.id)).to be(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if investment belongs to the poll's budget" do
|
||||||
|
expect(poll_ballot.add_investment(investment.id)).to be(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Already added" do
|
||||||
|
it "is not valid if already exists" do
|
||||||
|
poll_ballot.add_investment(investment.id)
|
||||||
|
expect(poll_ballot.add_investment(investment.id)).to be(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if does not already exist" do
|
||||||
|
expect(poll_ballot.add_investment(investment.id)).to be(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#find_investment" do
|
||||||
|
it "returns the investment if found" do
|
||||||
|
expect(poll_ballot.find_investment(investment.id)).to eq(investment)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "finds investments with trailing zeros" do
|
||||||
|
expect(poll_ballot.find_investment("0#{investment.id}")).to eq(investment)
|
||||||
|
expect(poll_ballot.find_investment("00#{investment.id}")).to eq(investment)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user