fixes conflicts with budgets

This commit is contained in:
rgarcia
2017-01-05 13:14:09 +01:00
58 changed files with 409 additions and 389 deletions

View File

@@ -76,7 +76,7 @@ $check: #46DB91;
$proposals: #FFA42D;
$proposals-dark: #794500;
$budget: #454372;
$budget: #7E328A;
$budget-hover: #7571BF;
$highlight: #E7F2FC;

View File

@@ -221,47 +221,6 @@ a {
float: left;
}
//.table-fixed {
// table-layout: fixed;
//=======
//.tabs-content {
// border: 0;
//}
//
//.tabs {
// border: {
// left: 0;
// right: 0;
// top: 0;
// };
// margin-bottom: $line-height;
//
// .tabs-title > a {
// color: $text-medium;
// margin-bottom: rem-calc(-1);
// margin-right: $line-height;
//
// &[aria-selected='true'],
// &.is-active {
// color: $brand;
// border-bottom: 2px solid $brand;
// font-weight: bold;
// }
// }
//
// h2 {
// font-size: $base-font-size;
// }
//}
//
//.no-max-width {
// max-width: none;
//}
//
//.button.float-right ~ .button.float-right {
// margin: 0 $line-height/2;
//}
.tabs-content {
border: 0;
}

View File

@@ -856,9 +856,22 @@
.expanded.budget {
background: $budget;
h1, p, a {
h1, h2, p, a.back, .icon-angle-left {
color: white;
}
.button {
background: white;
color: $budget;
}
.info {
background: #6A2A72;
@include breakpoint(medium) {
border-top: rem-calc(6) solid #54225C;
}
}
}
.jumbo-budget {

View File

@@ -2,12 +2,16 @@ class Valuation::BudgetsController < Valuation::BaseController
include FeatureFlags
feature_flag :budgets
has_filters %w{current finished}, only: :index
load_and_authorize_resource
def index
@budgets = Budget.send(@current_filter).order(created_at: :desc).page(params[:page])
@budgets = @budgets.current.order(created_at: :desc).page(params[:page])
@investments_with_valuation_open = {}
@budgets.each do |b|
@investments_with_valuation_open[b.id] = b.investments
.by_valuator(current_user.valuator.try(:id))
.valuation_open
.count
end
end
end

View File

@@ -46,7 +46,7 @@ module Abilities
can [:read, :create, :update, :destroy], Budget::Group
can [:read, :create, :update, :destroy], Budget::Heading
can [:hide, :update, :toggle_selection], Budget::Investment
can :valuate, Budget::Investment, budget: { phase: 'valuating' }
can :valuate, Budget::Investment
can :create, Budget::ValuatorAssignment
can [:search, :edit, :update, :create, :index, :destroy], Banner

View File

@@ -5,7 +5,8 @@ module Abilities
def initialize(user)
valuator = user.valuator
can [:read, :update, :valuate], SpendingProposal
can [:read, :update, :valuate], Budget::Investment, id: valuator.investment_ids, budget: { phase: 'valuating' }
can [:read, :update, :valuate], Budget::Investment, id: valuator.investment_ids
cannot [:update, :valuate], Budget::Investment, budget: { phase: 'finished' }
end
end
end

View File

@@ -36,14 +36,17 @@ class Budget
self.groups.include?(group)
end
def wrong_budget?(heading)
heading.budget_id != budget_id
end
def different_heading_assigned?(heading)
other_heading_ids = heading.group.heading_ids - [heading.id]
lines.where(heading_id: other_heading_ids).exists?
end
def valid_heading?(heading)
group = heading.group
return false if group.budget_id != budget_id
line = lines.where(heading_id: group.heading_ids).first
return false if line.present? && line.heading_id != heading.id
true
!wrong_budget?(heading) && !different_heading_assigned?(heading)
end
def has_lines_with_no_heading?

View File

@@ -9,28 +9,24 @@ class Budget
validates :ballot_id, :investment_id, :heading_id, :group_id, :budget_id, presence: true
validate :insufficient_funds
#needed? validate :different_geozone, :if => :district_proposal?
validate :unselected
validate :check_selected
validate :check_sufficient_funds
validate :check_valid_heading
before_validation :set_denormalized_ids
def insufficient_funds
def check_sufficient_funds
errors.add(:money, "insufficient funds") if ballot.amount_available(investment.heading) < investment.price.to_i
end
def different_geozone
errors.add(:heading, "different heading assigned") if (ballot.heading.present? && investment.heading != ballot.heading)
def check_valid_heading
errors.add(:heading, "This heading's budget is invalid, or a heading on the same group was already selected") unless ballot.valid_heading?(self.heading)
end
def unselected
def check_selected
errors.add(:investment, "unselected investment") unless investment.selected?
end
def heading_proposal?
investment.heading_id.present?
end
private
def set_denormalized_ids

View File

@@ -1,7 +1,6 @@
class Budget
class Heading < ActiveRecord::Base
belongs_to :group
belongs_to :geozone
has_many :investments
@@ -9,7 +8,7 @@ class Budget
validates :name, presence: true
validates :price, presence: true
delegate :budget, to: :group, allow_nil: true
delegate :budget, :budget_id, to: :group, allow_nil: true
scope :order_by_group_name, -> { includes(:group).order('budget_groups.name', 'budget_headings.name') }

View File

@@ -26,7 +26,7 @@ class Budget
validates :heading_id, presence: true
validates_presence_of :unfeasibility_explanation, if: :unfeasibility_explanation_required?
validates :title, length: { in: 4 .. Budget::Investment.title_max_length }
validates :title, length: { in: 4..Budget::Investment.title_max_length }
validates :description, length: { maximum: Budget::Investment.description_max_length }
validates :terms_of_service, acceptance: { allow_nil: false }, on: :create
@@ -53,7 +53,7 @@ class Budget
scope :by_tag, -> (tag_name) { tagged_with(tag_name) }
scope :by_valuator, -> (valuator_id) { where("budget_valuator_assignments.valuator_id = ?", valuator_id).joins(:valuator_assignments) }
scope :for_render, -> { includes(heading: :geozone) }
scope :for_render, -> { includes(:heading) }
before_save :calculate_confidence_score
before_validation :set_responsible_name

View File

@@ -3,10 +3,12 @@
<%= f.text_field :name, maxlength: Budget.title_max_length %>
<% Budget::PHASES.each do |phase| %>
<%= f.cktext_area "description_#{phase}", maxlength: Budget.description_max_length, ckeditor: { language: I18n.locale } %>
<div class="margin-top">
<%= f.cktext_area "description_#{phase}", maxlength: Budget.description_max_length, ckeditor: { language: I18n.locale } %>
</div>
<% end %>
<div class="row">
<div class="row margin-top">
<div class="small-12 medium-9 column">
<%= f.select :phase, budget_phases_select_options %>
</div>

View File

@@ -1,76 +1,67 @@
<div class="small-12 column">
<table>
<thead>
<tr>
<th colspan="3" class="with-button">
<%= group.name %>
<%= link_to t("admin.budgets.form.add_heading"), "#", class: "button float-right js-toggle-link", data: { "toggle-selector" => "#group-#{group.id}-new-heading-form" } %>
</th>
</tr>
<table>
<thead>
<tr>
<th colspan="3" class="with-button">
<%= group.name %>
<%= link_to t("admin.budgets.form.add_heading"), "#", class: "button float-right js-toggle-link", data: { "toggle-selector" => "#group-#{group.id}-new-heading-form" } %>
</th>
</tr>
<% if headings.blank? %>
<tbody>
<tr>
<td colspan="3">
<div class="callout primary">
<%= t("admin.budgets.form.no_heading") %>
</div>
</td>
</tr>
<% else %>
<tr>
<th><%= t("admin.budgets.form.table_heading") %></th>
<th><%= t("admin.budgets.form.table_amount") %></th>
<th><%= t("admin.budgets.form.table_geozone") %></th>
</tr>
</thead>
<tbody>
<% end %>
<!-- new heading form -->
<tr id="group-<%= group.id %>-new-heading-form" style="display:none">
<td colspan="3">
<%= form_for [:admin, @budget, group, Budget::Heading.new], remote: true do |f| %>
<label><%= t("admin.budgets.form.heading") %></label>
<%= f.text_field :name,
label: false,
maxlength: 50,
placeholder: t("admin.budgets.form.heading") %>
<div class="row">
<div class="small-12 medium-6 column">
<label><%= t("admin.budgets.form.amount") %></label>
<%= f.text_field :price,
label: false,
maxlength: 8,
placeholder: t("admin.budgets.form.amount") %>
</div>
<div class="small-12 medium-6 column">
<label><%= t("admin.budgets.form.geozone") %></label>
<%= f.select :geozone_id, geozone_select_options, {include_blank: t("geozones.none"), label: false} %>
</div>
</div>
<%= f.submit t("admin.budgets.form.save_heading"), class: "button success" %>
<% end %>
</td>
</tr>
<!-- /. new heading form -->
<!-- headings list -->
<% headings.each do |heading| %>
<% if headings.blank? %>
<tbody>
<tr>
<td>
<%= heading.name %>
</td>
<td>
<%= heading.price %>
</td>
<td>
<%= geozone_name_from_id heading.geozone_id %>
<td colspan="3">
<div class="callout primary">
<%= t("admin.budgets.form.no_heading") %>
</div>
</td>
</tr>
<% else %>
<tr>
<th><%= t("admin.budgets.form.table_heading") %></th>
<th><%= t("admin.budgets.form.table_amount") %></th>
</tr>
</thead>
<tbody>
<% end %>
<!-- new heading form -->
<tr id="group-<%= group.id %>-new-heading-form" style="display:none">
<td colspan="3">
<%= form_for [:admin, @budget, group, Budget::Heading.new], remote: true do |f| %>
<label><%= t("admin.budgets.form.heading") %></label>
<%= f.text_field :name,
label: false,
maxlength: 50,
placeholder: t("admin.budgets.form.heading") %>
<div class="row">
<div class="small-12 medium-6 column">
<label><%= t("admin.budgets.form.amount") %></label>
<%= f.text_field :price,
label: false,
maxlength: 8,
placeholder: t("admin.budgets.form.amount") %>
</div>
</div>
<%= f.submit t("admin.budgets.form.save_heading"), class: "button success" %>
<% end %>
<!-- /. headings list -->
</td>
</tr>
<!-- /. new heading form -->
<!-- headings list -->
<% headings.each do |heading| %>
<tr>
<td>
<%= heading.name %>
</td>
<td>
<%= heading.price %>
</td>
</tr>
<% end %>
<!-- /. headings list -->
</tbody>
</table>
</div>
</div>

View File

@@ -1,34 +1,32 @@
<div class="small-12 column">
<h3 class="inline-block"><%= t('admin.budgets.show.groups') %></h3>
<% if groups.blank? %>
<div class="callout primary">
<%= t("admin.budgets.form.no_groups") %>
<strong><%= link_to t("admin.budgets.form.add_group"), "#",
class: "js-toggle-link",
data: { "toggle-selector" => "#new-group-form" } %></strong>
</div>
<% else %>
<%= link_to t("admin.budgets.form.add_group"), "#", class: "button float-right js-toggle-link", data: { "toggle-selector" => "#new-group-form" } %>
<% end %>
<h3 class="inline-block"><%= t('admin.budgets.show.groups', count: groups.count) %></h3>
<% if groups.blank? %>
<div class="callout primary">
<%= t("admin.budgets.form.no_groups") %>
<strong><%= link_to t("admin.budgets.form.add_group"), "#",
class: "js-toggle-link",
data: { "toggle-selector" => "#new-group-form" } %></strong>
</div>
<% else %>
<%= link_to t("admin.budgets.form.add_group"), "#", class: "button float-right js-toggle-link", data: { "toggle-selector" => "#new-group-form" } %>
<% end %>
<%= form_for [:admin, @budget, Budget::Group.new], html: {id: "new-group-form", style: "display:none"}, remote: true do |f| %>
<div class="input-group">
<span class="input-group-label">
<label><%= f.label :name,t("admin.budgets.form.group") %></label>
</span>
<%= f.text_field :name,
label: false,
maxlength: 50,
placeholder: t("admin.budgets.form.group") %>
<div class="input-group-button">
<%= f.submit t("admin.budgets.form.create_group"), class: "button success" %>
</div>
<%= form_for [:admin, @budget, Budget::Group.new], html: {id: "new-group-form", style: "display:none"}, remote: true do |f| %>
<div class="input-group">
<span class="input-group-label">
<label><%= f.label :name,t("admin.budgets.form.group") %></label>
</span>
<%= f.text_field :name,
label: false,
maxlength: 50,
placeholder: t("admin.budgets.form.group") %>
<div class="input-group-button">
<%= f.submit t("admin.budgets.form.create_group"), class: "button success" %>
</div>
<% end %>
</div>
<% end %>
<% groups.each do |group| %>
<div class="row" id="<%= dom_id(group) %>">
<%= render "admin/budgets/group", group: group, headings: group.headings %>
</div>
<% end %>
</div>
<% groups.each do |group| %>
<div id="<%= dom_id(group) %>">
<%= render "admin/budgets/group", group: group, headings: group.headings %>
</div>
<% end %>

View File

@@ -1,3 +1,8 @@
<%= link_to admin_budgets_path do %>
<span class="icon-angle-left"></span>
<%= t('shared.back') %>
<% end %>
<div class="row">
<div class="small-12 medium-9 column">
<h2><%= t("admin.budgets.edit.title") %></h2>

View File

@@ -6,23 +6,39 @@
<%= render 'shared/filter_subnav', i18n_namespace: "admin.budgets.index" %>
<h3><%= page_entries_info @budgets %></h3>
<table>
<% @budgets.each do |budget| %>
<tr id="<%= dom_id(budget) %>" class="budget">
<td>
<%= link_to budget.name, admin_budget_budget_investments_path(budget_id: budget.id) %>
</td>
<td class="small">
<%= t("budget.phase.#{budget.phase}") %>
</td>
<td class="small">
<%= link_to t("admin.budgets.index.info_link"), admin_budget_path(budget) %>
</td>
<thead>
<tr>
<th><%= t("admin.budgets.index.table_name") %></th>
<th><%= t("admin.budgets.index.table_phase") %></th>
<th><%= t("admin.budgets.index.table_investments") %></th>
<th><%= t("admin.budgets.index.table_edit_groups") %></th>
<th><%= t("admin.budgets.index.table_edit_budget") %></th>
</tr>
<% end %>
</thead>
<tbody>
<% @budgets.each do |budget| %>
<tr id="<%= dom_id(budget) %>" class="budget">
<td>
<%= budget.name %>
</td>
<td class="small">
<%= t("budget.phase.#{budget.phase}") %>
</td>
<td class="small">
<%= link_to t("admin.budgets.index.budget_investments"), admin_budget_budget_investments_path(budget_id: budget.id) %>
</td>
<td class="small">
<%= link_to t("admin.budgets.index.edit_groups"), admin_budget_path(budget) %>
</td>
<td class="small">
<%= link_to t("admin.budgets.index.edit_budget"), edit_admin_budget_path(budget) %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= paginate @budgets %>

View File

@@ -1,16 +1,10 @@
<div class="row">
<div class="small-12 medium-9 column">
<h2><%= @budget.name %> <small><%= link_to(t('shared.edit'), edit_admin_budget_path(@budget)) %></small></h2>
<%= link_to admin_budgets_path do %>
<span class="icon-angle-left"></span>
<%= t('shared.back') %>
<% end %>
<%= @budget.description %>
<h2><%= @budget.name %></h2>
<p>
<strong><%= t('admin.budgets.show.phase') %>:</strong> <%= t("budget.phase.#{@budget.phase}") %> |
<strong><%= t('admin.budgets.show.currency') %>:</strong> <%= @budget.currency_symbol %>
</p>
</div>
</div>
<div id="<%= dom_id @budget %>_groups" class="row">
<div id="<%= dom_id @budget %>_groups">
<%= render "groups", groups: @budget.groups %>
</div>

View File

@@ -1,12 +1,18 @@
<div class="row budget-investments-scope margin-bottom">
<div class="expanded budget no-margin-top">
<div class="row">
<div class="small-12 medium-9 column padding">
<%= link_to budget_path(@budget), class: "back" do %>
<span class="icon-angle-left"></span>
<%= t('shared.back') %>
<% end %>
<h2><%= t("budgets.groups.show.title") %></h2>
</div>
</div>
</div>
<div class="row margin-top">
<div id="select-district" class="small-12 medium-7 column select-district">
<%= link_to budget_path(@budget), class: "back" do %>
<span class="icon-angle-left"></span>
<%= t('shared.back') %>
<% end %>
<h2><%= t("budgets.groups.show.title") %></h2>
<div class="row">
<% @group.headings.each_slice(7) do |slice| %>
<div class="small-6 medium-4 column end">

View File

@@ -7,8 +7,8 @@
</div>
<div class="row margin-top">
<div class="small-12 medium-6 column">
<table class="table-fixed">
<div class="small-12 medium-9 column">
<table>
<thead>
<th><%= t('budget.index.name') %></th>
<th><%= t('budget.index.phase') %></th>

View File

@@ -2,8 +2,9 @@
<%= render 'shared/errors', resource: @investment %>
<div class="row">
<div class="small-12 column">
<%= f.select :heading_id, budget_heading_select_options(@budget), include_blank: true %>
<div class="small-12 medium-8 column">
<%= f.label :heading_id, t("budget.investments.form.heading") %>
<%= f.select :heading_id, budget_heading_select_options(@budget), {include_blank: true, label: false} %>
</div>
<div class="small-12 column">
@@ -58,8 +59,8 @@
<% end %>
<div class="actions small-12 column">
<%= f.submit(nil, class: "button") %>
<div class="actions small-12 medium-6 large-4 end column">
<%= f.submit(class: "button expanded", value: t("budget.investments.form.submit_buttons.#{action_name}")) %>
</div>
</div>
<% end %>

View File

@@ -20,7 +20,7 @@
<div class="row progress-votes">
<div class="small-12 column">
<div class="progress-bar-nav" data-fixed-bar>
<h1 class="inline-block"><%= @filter_geozone_name %></h1>
<h1 class="inline-block"><%= @heading.name %></h1>
<div id="check-ballot" style="display: none;">
<% if can? :show, @ballot %>
<%= link_to t("budget.investments.header.check_ballot"),

View File

@@ -21,7 +21,7 @@
method: "post",
remote: true,
"aria-hidden" => css_for_aria_hidden(reason) do %>
<%= t("budget.investments.investment.vote") %>
<%= t("budget.investments.investment.give_support") %>
<% end %>
<% end %>
</div>

View File

@@ -1,7 +1,6 @@
<div class="budget-investment-new">
<div class="small-12 medium-9 column end">
<h1 class=""><%= t("management.budget_investments.create") %></h1>
<div class="budget-investment-new row">
<div class="small-12 medium-9 column">
<h1><%= t("management.budget_investments.create") %></h1>
<%= render '/budgets/investments/form', form_url: budget_investments_path(@budget) %>
</div>

View File

@@ -1,23 +1,38 @@
<div class="expanded budget no-margin-top padding">
<div class="row">
<div class="small-12 medium-9 column">
<%= link_to budgets_path do %>
<div class="expanded budget no-margin-top">
<div class="row" data-equalizer>
<div class="small-12 medium-9 column padding" data-equalizer-watch>
<%= link_to budgets_path, class: "back" do %>
<span class="icon-angle-left"></span>
<%= t('shared.back') %>
<% end %>
<h1><%= @budget.name %>
<small><%= t("budgets.phases.#{@budget.phase}") %></small>
</h1>
<h1><%= @budget.name %></h1>
<%= @budget.description %>
</div>
<div class="small-12 medium-3 column info padding" data-equalizer-watch>
<p>
<strong><%= t('budget.show.phase') %></strong>
<br>
<%= t("budgets.phases.#{@budget.phase}") %>
</p>
<% if @budget.accepting? %>
<% if current_user && current_user.level_two_or_three_verified? %>
<%= link_to t("budget.investments.index.sidebar.create"), new_budget_investment_path(@budget), class: "button margin-top" %>
<% else %>
<div class="callout warning margin-top">
<%= t("budget.investments.index.sidebar.verified_only",
verify: link_to(t("budget.investments.index.sidebar.verify_account"), verification_path)).html_safe %>
</div>
<% end %>
<% end %>
</div>
</div>
</div>
<div class="row margin-top">
<div class="small-12 medium-6 column">
<div class="small-12 medium-9 column">
<table class="table-fixed">
<thead>
<th><%= t('budget.show.group') %></th>

View File

@@ -1,4 +1,8 @@
<%= link_to "#{t('valuation.budget_investments.show.title')} #{@investment.id}", valuation_budget_budget_investment_path(@budget, @investment), class: 'back' %>
<%= link_to valuation_budget_budget_investment_path(@budget, @investment), class: 'back' do %>
<span class="icon-angle-left"></span>
<%= "#{t('valuation.budget_investments.show.title')} #{@investment.id}"%>
<% end %>
<h2><%= t("valuation.budget_investments.edit.dossier") %></h2>
<%= form_for(@investment, url: valuate_valuation_budget_budget_investment_path(@budget, @investment), html: {id: "valuation_budget_investment_edit_form"}) do |f| %>

View File

@@ -1,4 +1,7 @@
<h2><%= @budget.name %> - <%= t("valuation.budget_investments.index.title") %></h2>
<h2>
<%= @budget.name %> - <%= t("valuation.budget_investments.index.title") %>
<small><%= t('valuation.budget_investments.index.assigned_to', valuator: current_user.name) %></small>
</h2>
<div class="row collapse">
<% @heading_filters.each_slice(8) do |slice| %>
@@ -18,25 +21,34 @@
<h3><%= page_entries_info @investments %></h3>
<table>
<% @investments.each do |investment| %>
<tr id="<%= dom_id(investment) %>">
<td>
<strong><%= investment.id %></strong>
</td>
<td>
<%= link_to investment.title, valuation_budget_budget_investment_path(@budget, investment) %>
</td>
<td class="small">
<%= link_to t("valuation.budget_investments.index.edit"), edit_valuation_budget_budget_investment_path(@budget, investment) %>
</td>
<td class="small">
<%= assigned_valuators_info(investment.valuators) %>
</td>
<td class="small">
<%= investment.heading.name %>
</td>
<thead>
<tr>
<th><%= t("valuation.budget_investments.index.table_id") %></th>
<th><%= t("valuation.budget_investments.index.table_title") %></th>
<th><%= t("valuation.budget_investments.index.table_heading_name") %></th>
<th><%= t("valuation.budget_investments.index.table_actions") %></th>
</tr>
<% end %>
</thead>
<tbody>
<% @investments.each do |investment| %>
<tr id="<%= dom_id(investment) %>">
<td>
<strong><%= investment.id %></strong>
</td>
<td>
<%= link_to investment.title, valuation_budget_budget_investment_path(@budget, investment) %>
</td>
<td class="small">
<%= investment.heading.name %>
</td>
<td class="small">
<%= link_to t("valuation.budget_investments.index.edit"),
edit_valuation_budget_budget_investment_path(@budget, investment),
class: "button hollow expanded" %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= paginate @investments %>

View File

@@ -1,4 +1,7 @@
<%= render "shared/back_link" %>
<%= link_to valuation_budget_budget_investments_path do %>
<span class="icon-angle-left"></span>
<%= t('shared.back') %>
<% end %>
<h2><%= t("valuation.budget_investments.show.title") %> <%= @investment.id %> </h2>
<h1><%= @investment.title %></h1>

View File

@@ -1,17 +1,36 @@
<h2 class="inline-block"><%= t("valuation.budgets.index.title") %></h2>
<%= render 'shared/filter_subnav', i18n_namespace: "valuation.budgets.index" %>
<h3><%= page_entries_info @budgets %></h3>
<table>
<% @budgets.each do |budget| %>
<tr id="<%= dom_id(budget) %>" class="budget">
<td>
<%= link_to budget.name, valuation_budget_budget_investments_path(budget_id: budget.id) %>
</td>
<thead>
<tr>
<th><%= t("valuation.budgets.index.table_name") %></th>
<th><%= t("valuation.budgets.index.table_phase") %></th>
<th><%= t("valuation.budgets.index.table_assigned_investments_valuation_open") %></th>
<th><%= t("valuation.budgets.index.table_actions") %></th>
</tr>
<% end %>
</thead>
<tbody>
<% @budgets.each do |budget| %>
<tr id="<%= dom_id(budget) %>" class="budget">
<td>
<%= budget.name %>
</td>
<td>
<%= t("budget.phase.#{budget.phase}") %>
</td>
<td>
<%= @investments_with_valuation_open[budget.id] %>
</td>
<td>
<%= link_to t("valuation.budgets.index.evaluate"),
valuation_budget_budget_investments_path(budget_id: budget.id),
class: "button hollow expanded" %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= paginate @budgets %>
<%= paginate @budgets %>

View File

@@ -50,7 +50,7 @@
<ul>
<li>
<%= t("verification.letter.new.office",
office: link_to(t("verification.letter.new.offices"), t("verification.letter.new.offices_url"),
office: link_to(t("verification.letter.new.offices"), setting['verification_offices_url'],
target: "blank")).html_safe %>
</li>
<li>

View File

@@ -12,7 +12,7 @@
<div data-alert class="callout success">
<%= t("verification.letter.create.flash.success_html",
offices: link_to(t("verification.letter.create.flash.offices"),
t("verification.letter.create.flash.offices_url"),
setting['verification_offices_url'],
title: t('shared.target_blank_html'),
target: "_blank")).html_safe
%>

View File

@@ -6,7 +6,7 @@
</button>
<%= t("verification.residence.new.error_verifying_census",
offices: link_to( t("verification.residence.new.error_verifying_census_offices"),
t("verification.residence.new.error_verifying_census_offices_url"), title: t('shared.target_blank_html'), target: "_blank")).html_safe %>
setting['verification_offices_url'], title: t('shared.target_blank_html'), target: "_blank")).html_safe %>
</div>
<% else %>

View File

@@ -13,7 +13,7 @@ Devise.setup do |config|
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
if Rails.env.test? || !ActiveRecord::Base.connection.table_exists?('settings')
config.mailer_sender = "noreply@example.org"
config.mailer_sender = "noreply@consul.dev"
else
config.mailer_sender = "#{Setting['mailer_from_name']} <#{Setting['mailer_from_address']}>"
end

View File

@@ -47,7 +47,7 @@ es:
description_reviewing: "Descripción durante la fase de revisión"
description_selecting: "Descripción durante la fase de selección"
description_valuating: "Descripción durante la fase de evaluación"
description_balloting: "Descripción duratne la fase de votación"
description_balloting: "Descripción durante la fase de votación"
description_reviewing_ballots: "Descripción durante la fase de revisión de votos"
description_finished: "Descripción cuando el presupuesto ha finalizado"
phase: "Fase"

View File

@@ -62,10 +62,17 @@ en:
index:
title: Participatory budgets
new_link: Create new budget
info_link: Info
filters:
open: Open
current: Open
finished: Finished
budget_investments: See budget investments
table_name: Name
table_phase: Phase
table_investments: Investments
table_edit_groups: Headings groups
table_edit_budget: Edit
edit_groups: Edit headings groups
edit_budget: Edit budget
create:
notice: New participatory budget created successfully!
update:
@@ -75,9 +82,9 @@ en:
new:
title: New participatory budget
show:
phase: Current phase
currency: Currency
groups: Groups of budget headings
groups:
one: 1 Group of budget headings
other: "%{count} Groups of budget headings"
form:
group: Group name
no_groups: No groups created yet. Each user will be able to vote in only one heading per group.
@@ -88,10 +95,8 @@ en:
amount: Amount
save_heading: Save heading
no_heading: This group has no assigned heading.
geozone: Scope of operation
table_heading: Heading
table_amount: Amount
table_geozone: Scope of operation
budget_investments:
index:
heading_filter_all: All headings

View File

@@ -62,10 +62,17 @@ es:
index:
title: Presupuestos participativos
new_link: Crear nuevo presupuesto
info_link: Info
filters:
open: Abiertos
current: Abiertos
finished: Terminados
budget_investments: Ver propuestas de inversión
table_name: Nombre
table_phase: Fase
table_investments: Propuestas de inversión
table_edit_groups: Grupos de partidas
table_edit_budget: Editar
edit_groups: Editar grupos de partidas
edit_budget: Editar presupuesto
create:
notice: ¡Nueva campaña de presupuestos participativos creada con éxito!
update:
@@ -75,9 +82,9 @@ es:
new:
title: Nuevo presupuesto ciudadano
show:
phase: Fase actual
currency: Divisa
groups: Grupos de partidas presupuestarias
groups:
one: 1 Grupo de partidas presupuestarias
other: "%{count} Grupos de partidas presupuestarias"
form:
group: Nombre del grupo
no_groups: No hay grupos creados todavía. Cada usuario podrá votar en una sola partida de cada grupo.
@@ -88,10 +95,8 @@ es:
amount: Cantidad
save_heading: Guardar partida
no_heading: Este grupo no tiene ninguna partida asignada.
geozone: Ámbito de actuación
table_heading: Partida
table_amount: Cantidad
table_geozone: Ámbito de actuación
budget_investments:
index:
heading_filter_all: Todas las partidas

View File

@@ -118,7 +118,7 @@ en:
one: 1 support
other: "%{count} supports"
zero: No supports
vote: Vote
give_support: Support
header:
check_ballot: Check my ballot
different_heading_assigned_html: "You have active votes in another heading: %{heading_link}"
@@ -127,3 +127,4 @@ en:
heading: Heading
price: Price
no_heading: No Heading
phase: Actual phase

View File

@@ -117,7 +117,7 @@ es:
one: 1 apoyo
other: "%{count} apoyos"
zero: Sin apoyos
vote: Votar
give_support: Apoyar
header:
check_ballot: Revisar mis votos
different_heading_assigned_html: "Ya apoyaste propuestas de otra sección del presupuesto: %{heading_link}"
@@ -126,3 +126,4 @@ es:
heading: Partida
price: Cantidad
no_heading: Sin línea
phase: Fase actual

View File

@@ -441,7 +441,6 @@ en:
collective: Collective
flag: Flag as inappropriate
hide: Hide
edit: Editar
print:
print_button: Print this info
search: Search

View File

@@ -445,7 +445,6 @@ es:
print_button: Imprimir esta información
search: Buscar
show: Mostrar
edit: Editar
suggest:
debate:
found:

View File

@@ -1497,7 +1497,6 @@ fr:
create:
flash:
offices: Bureaux de soutien aux citoyens
offices_url: http://offices.consul
success_html: Merci d'avoir demandé votre <b>code de sécurité maximum (uniquement
requis pour le vote final)</b>. Dans quelques jours, nous vous enverrons
un courrier vers votre adresse postale. Vous pouvez le réclamer dans n'importe
@@ -1512,7 +1511,6 @@ fr:
go_to_index: Voir les propositions
office: Vérifier dans n'importe quel %{office}
offices: Bureaux de soutien aux citoyens
offices_url: http://offices.consul
send_letter: Envoyer une lettre avec le code
title: Félicitations
user_permission_info: Avec votre compte, vous pouvez

View File

@@ -1518,7 +1518,6 @@ pt-BR:
create:
flash:
offices: Escritórios de Apoio ao Cidadão
offices_url: http://offices.consul
success_html: Obrigado por solicitar seu <b>código de segurança máxima (somente
solicitado em votações finais)</b>. Em poucos dias nós lhe enviaremos
para seu endereço relacionado nos dados que temos em arquivo. Por favor
@@ -1534,7 +1533,6 @@ pt-BR:
go_to_index: Ver propostas
office: Verificar em qualquer %{office}
offices: Escritórios de Apoio ao Cidadão
offices_url: http://offices.consul
send_letter: Envie-me uma carta com o código
title: Parabéns!
user_permission_info: Com a sua conta você pode...

View File

@@ -35,4 +35,5 @@ en:
mailer_from_name: Origin email name
mailer_from_address: Origin email address
meta_description: "Site description (SEO)"
meta_keywords: "Keywords (SEO)"
meta_keywords: "Keywords (SEO)"
verification_offices_url: Verification offices URL

View File

@@ -35,4 +35,5 @@ es:
mailer_from_name: Nombre email remitente
mailer_from_address: Dirección email remitente
meta_description: "Descripción del sitio (SEO)"
meta_keywords: "Palabras clave (SEO)"
meta_keywords: "Palabras clave (SEO)"
verification_offices_url: URL oficinas verificación

View File

@@ -9,8 +9,13 @@ en:
index:
title: Participatory budgets
filters:
open: Open
current: Open
finished: Finished
table_name: Name
table_phase: Phase
table_assigned_investments_valuation_open: Investment projects assigned with valuation open
table_actions: Actions
evaluate: Evaluate
budget_investments:
index:
headings_filter_all: All headings
@@ -18,12 +23,17 @@ en:
valuation_open: Open
valuating: Under valuation
valuation_finished: Valuation finished
assigned_to: "Assigned to %{valuator}"
title: Investment projects
edit: Edit
edit: Edit dossier
valuators_assigned:
one: Assigned valuator
other: "%{count} valuators assigned"
no_valuators_assigned: No valuators assigned
table_id: ID
table_title: Title
table_heading_name: Heading name
table_actions: Actions
show:
back: Back
title: Investment project

View File

@@ -9,8 +9,13 @@ es:
index:
title: Presupuestos participativos
filters:
open: Abiertos
current: Abiertos
finished: Terminados
table_name: Nombre
table_phase: Fase
table_assigned_investments_valuation_open: Prop. Inv. asignadas en evaluación
table_actions: Acciones
evaluate: Evaluar
budget_investments:
index:
headings_filter_all: Todas las partidas
@@ -19,11 +24,16 @@ es:
valuating: En evaluación
valuation_finished: Evaluación finalizada
title: Propuestas de inversión
edit: Editar
assigned_to: "Asignadas a %{valuator}"
edit: Editar informe
valuators_assigned:
one: Evaluador asignado
other: "%{count} evaluadores asignados"
no_valuators_assigned: Sin evaluador
table_id: ID
table_title: Título
table_heading_name: Nombre de la partida
table_actions: Acciones
show:
back: Volver
title: Propuesta de inversión

View File

@@ -21,7 +21,6 @@ en:
create:
flash:
offices: Citizen Support Offices
offices_url: http://offices.consul
success_html: Thank you for requesting your <b>maximum security code (only required for the final votes)</b>. In a few days we will send it to the address featuring in the data we have on file. Please remember that, if you prefer, you can collect your code from any of the %{offices}.
edit:
see_all: See proposals
@@ -33,7 +32,6 @@ en:
go_to_index: See proposals
office: Verify in any %{office}
offices: Citizen Support Offices
offices_url: http://offices.consul
send_letter: Send me a letter with the code
title: Congratulations!
user_permission_info: With your account you can...
@@ -65,7 +63,6 @@ en:
error_not_allowed_postal_code: In order to be verified, you must be registered.
error_verifying_census: The Census was unable to verify your information. Please confirm that your census details are correct by calling to City Council or visit one %{offices}.
error_verifying_census_offices: Citizen Support Office
error_verifying_census_offices_url: http://offices.consul
form_errors: prevented the verification of your residence
postal_code: Postcode
postal_code_note: To verify your account you must be registered

View File

@@ -21,7 +21,6 @@ es:
create:
flash:
offices: Oficinas de Atención al Ciudadano
offices_url: http://offices.consul
success_html: Antes de las votaciones recibirás una carta con las instrucciones para verificar tu cuenta.<br> Recuerda que puedes ahorrar el envío verificándote presencialmente en cualquiera de las %{offices}.
edit:
see_all: Ver propuestas
@@ -33,7 +32,6 @@ es:
go_to_index: Ver propuestas
office: Verificarte presencialmente en cualquier %{office}
offices: Oficina de Atención al Ciudadano
offices_url: http://offices.consul
send_letter: Solicitar una carta por correo postal
title: "¡Felicidades!"
user_permission_info: Con tu cuenta ya puedes...
@@ -65,7 +63,6 @@ es:
error_not_allowed_postal_code: Para verificarte debes estar empadronado.
error_verifying_census: El Padrón no pudo verificar tu información. Por favor, confirma que tus datos de empadronamiento sean correctos llamando al Ayuntamiento o visitando una %{offices}.
error_verifying_census_offices: oficina de Atención al ciudadano
error_verifying_census_offices_url: http://offices.consul
form_errors: evitaron verificar tu residencia
postal_code: Código postal
postal_code_note: Para verificar tus datos debes estar empadronado

View File

@@ -38,6 +38,7 @@ Setting.create(key: 'mailer_from_name', value: 'Consul')
Setting.create(key: 'mailer_from_address', value: 'noreply@consul.dev')
Setting.create(key: 'meta_description', value: 'Citizen Participation and Open Government Application')
Setting.create(key: 'meta_keywords', value: 'citizen participation, open government')
Setting.create(key: 'verification_offices_url', value: 'http://oficinas-atencion-ciudadano.url/')
puts "Creating Geozones"
('A'..'Z').each { |i| Geozone.create(name: "District #{i}", external_code: i.ord, census_code: i.ord) }
@@ -372,7 +373,7 @@ tags = Faker::Lorem.words(10)
description: "<p>#{Faker::Lorem.paragraphs.join('</p><p>')}</p>",
created_at: rand((Time.now - 1.week) .. Time.now),
feasibility: %w{undecided unfeasible feasible feasible feasible feasible}.sample,
unfeasibility_explanation: "<p>#{Faker::Lorem.paragraphs.join('</p><p>')}</p>",
unfeasibility_explanation: Faker::Lorem.paragraph,
valuation_finished: [false, true].sample,
tag_list: tags.sample(3).join(','),
price: rand(1 .. 100) * 100000,

View File

@@ -0,0 +1,5 @@
class RemoveGeozoneIdFromBudgetHeadings < ActiveRecord::Migration
def change
remove_column :budget_headings, :geozone_id
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170102080432) do
ActiveRecord::Schema.define(version: 20170103170147) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -107,9 +107,8 @@ ActiveRecord::Schema.define(version: 20170102080432) do
create_table "budget_headings", force: :cascade do |t|
t.integer "group_id"
t.integer "geozone_id"
t.string "name", limit: 50
t.integer "price", limit: 8
t.string "name", limit: 50
t.integer "price", limit: 8
end
add_index "budget_headings", ["group_id"], name: "index_budget_headings_on_group_id", using: :btree

View File

@@ -91,3 +91,6 @@ Setting['direct_message_max_per_day'] = 3
# Email settings
Setting['mailer_from_name'] = 'Consul'
Setting['mailer_from_address'] = 'noreply@consul.dev'
# Verification settings
Setting['verification_offices_url'] = 'http://oficinas-atencion-ciudadano.url/'

View File

@@ -47,7 +47,7 @@ feature 'Admin budgets' do
expect(page).to_not have_content(budget4.name)
expect(page).to have_content(budget5.name)
click_link 'Current'
click_link 'Open'
expect(page).to have_content(budget1.name)
expect(page).to have_content(budget2.name)
expect(page).to have_content(budget3.name)
@@ -55,8 +55,8 @@ feature 'Admin budgets' do
expect(page).to_not have_content(budget5.name)
end
scenario 'Current filter is properly highlighted' do
filters_links = {'current' => 'Current', 'finished' => 'Finished'}
scenario 'Open filter is properly highlighted' do
filters_links = {'current' => 'Open', 'finished' => 'Finished'}
visit admin_budgets_path
@@ -110,22 +110,29 @@ feature 'Admin budgets' do
visit admin_budgets_path
within("#budget_#{budget.id}") do
click_link 'Info'
click_link 'Edit headings groups'
end
expect(page).to have_content '0 Groups of budget headings'
expect(page).to have_content 'No groups created yet.'
click_link 'Add new group'
fill_in 'budget_group_name', with: 'General improvments'
fill_in 'budget_group_name', with: 'Health'
click_button 'Create group'
expect(page).to have_content '1 Group of budget headings'
expect(page).to have_content 'Health'
expect(page).to have_content 'Yearly participatory budget'
expect(page).to_not have_content 'No groups created yet.'
visit admin_budgets_path
click_link 'Yearly participatory budget'
within("#budget_#{budget.id}") do
click_link 'Edit headings groups'
end
expect(page).to have_content '1 Group of budget headings'
expect(page).to have_content 'Health'
expect(page).to have_content 'Yearly participatory budget'
expect(page).to_not have_content 'No groups created yet.'
end
@@ -153,7 +160,6 @@ feature 'Admin budgets' do
expect(page).to have_content 'District 9 reconstruction'
expect(page).to have_content '6785'
expect(page).to have_content 'All city'
end
end

View File

@@ -344,10 +344,8 @@ feature 'Budget Investments' do
global_group = create(:budget_group, budget: budget, name: 'Global Group')
global_heading = create(:budget_heading, group: global_group, name: 'Global Heading')
carabanchel = create(:geozone, name: "Carabanchel")
new_york = create(:geozone, name: "New York")
carabanchel_heading = create(:budget_heading, group: group, geozone: carabanchel, name: carabanchel.name)
new_york_heading = create(:budget_heading, group: group, geozone: new_york, name: new_york.name)
carabanchel_heading = create(:budget_heading, group: group, name: "Carabanchel")
new_york_heading = create(:budget_heading, group: group, name: "New York")
sp1 = create(:budget_investment, :selected, price: 1, heading: global_heading)

View File

@@ -54,34 +54,6 @@ feature 'Valuation budget investments' do
expect(investment10.title).to appear_before(investment1.title)
end
scenario 'Index shows assignments info' do
investment1 = create(:budget_investment, budget: @budget)
investment2 = create(:budget_investment, budget: @budget)
investment3 = create(:budget_investment, budget: @budget)
valuator1 = create(:valuator, user: create(:user))
valuator2 = create(:valuator, user: create(:user))
valuator3 = create(:valuator, user: create(:user))
investment1.valuator_ids = [@valuator.id]
investment2.valuator_ids = [@valuator.id, valuator1.id, valuator2.id]
investment3.valuator_ids = [@valuator.id, valuator3.id]
visit valuation_budget_budget_investments_path(@budget)
within("#budget_investment_#{investment1.id}") do
expect(page).to have_content("Rachel")
end
within("#budget_investment_#{investment2.id}") do
expect(page).to have_content("3 valuators assigned")
end
within("#budget_investment_#{investment3.id}") do
expect(page).to have_content("2 valuators assigned")
end
end
scenario "Index filtering by heading", :js do
group = create(:budget_group, budget: @budget)
heading1 = create(:budget_heading, name: "District 9", group: group)
@@ -262,7 +234,7 @@ feature 'Valuation budget investments' do
scenario 'Edit dossier' do
visit valuation_budget_budget_investments_path(@budget)
within("#budget_investment_#{@investment.id}") do
click_link "Edit"
click_link "Edit dossier"
end
fill_in 'budget_investment_price', with: '12345'
@@ -381,7 +353,7 @@ feature 'Valuation budget investments' do
scenario 'Validates price formats' do
visit valuation_budget_budget_investments_path(@budget)
within("#budget_investment_#{@investment.id}") do
click_link "Edit"
click_link "Edit dossier"
end
fill_in 'budget_investment_price', with: '12345,98'

View File

@@ -34,39 +34,6 @@ feature 'Valuation budgets' do
expect(page).to have_content(budget3.name)
expect(page).to have_content(budget4.name)
expect(page).to_not have_content(budget5.name)
click_link 'Finished'
expect(page).to_not have_content(budget1.name)
expect(page).to_not have_content(budget2.name)
expect(page).to_not have_content(budget3.name)
expect(page).to_not have_content(budget4.name)
expect(page).to have_content(budget5.name)
click_link 'Current'
expect(page).to have_content(budget1.name)
expect(page).to have_content(budget2.name)
expect(page).to have_content(budget3.name)
expect(page).to have_content(budget4.name)
expect(page).to_not have_content(budget5.name)
end
scenario 'Current filter is properly highlighted' do
filters_links = {'current' => 'Current', 'finished' => 'Finished'}
visit valuation_budgets_path
expect(page).to_not have_link(filters_links.values.first)
filters_links.keys.drop(1).each { |filter| expect(page).to have_link(filters_links[filter]) }
filters_links.each_pair do |current_filter, link|
visit valuation_budgets_path(filter: current_filter)
expect(page).to_not have_link(link)
(filters_links.keys - [current_filter]).each do |filter|
expect(page).to have_link(filters_links[filter])
end
end
end
end

View File

@@ -20,6 +20,7 @@ feature 'Verify Letter' do
end
scenario 'Go to office instead of send letter' do
Setting["verification_offices_url"] = "http://offices.consul"
user = create(:user, residence_verified_at: Time.current,
confirmed_phone: "611111111")

View File

@@ -66,6 +66,6 @@ describe "Abilities::Administrator" do
it { should be_able_to(:hide, Budget::Investment) }
it { should be_able_to(:valuate, create(:budget_investment, budget: create(:budget, phase: 'valuating'))) }
it { should_not be_able_to(:valuate, create(:budget_investment, budget: create(:budget, phase: 'finished'))) }
it { should be_able_to(:valuate, create(:budget_investment, budget: create(:budget, phase: 'finished'))) }
end

View File

@@ -10,8 +10,8 @@ describe "Abilities::Valuator" do
let(:assigned_investment) { create(:budget_investment, budget: create(:budget, phase: 'valuating')) }
before(:each) { assigned_investment.valuators << valuator }
let(:assigned_investment_not_valuating) { create(:budget_investment, budget: create(:budget, phase: 'finished')) }
before(:each) { assigned_investment_not_valuating.valuators << valuator }
let(:finished_assigned_investment) { create(:budget_investment, budget: create(:budget, phase: 'finished')) }
before(:each) { finished_assigned_investment.valuators << valuator }
it { should be_able_to(:read, SpendingProposal) }
it { should be_able_to(:update, SpendingProposal) }
@@ -23,6 +23,6 @@ describe "Abilities::Valuator" do
it { should be_able_to(:update, assigned_investment) }
it { should be_able_to(:valuate, assigned_investment) }
it { should_not be_able_to(:update, assigned_investment_not_valuating) }
it { should_not be_able_to(:valuate, assigned_investment_not_valuating) }
it { should_not be_able_to(:update, finished_assigned_investment) }
it { should_not be_able_to(:valuate, finished_assigned_investment) }
end

View File

@@ -23,29 +23,35 @@ describe Budget::Ballot do
end
it "returns the amount spent on all investments assigned to a specific heading" do
heading = create(:budget_heading)
budget = heading.group.budget
inv1 = create(:budget_investment, :selected, price: 10000, heading: heading)
inv2 = create(:budget_investment, :selected, price: 20000, heading: create(:budget_heading, group: heading.group))
inv3 = create(:budget_investment, :selected, price: 40000, heading: heading)
budget = create(:budget)
group1 = create(:budget_group, budget: budget)
group2 = create(:budget_group, budget: budget)
heading1 = create(:budget_heading, group: group1, price: 100000)
heading2 = create(:budget_heading, group: group2, price: 200000)
inv1 = create(:budget_investment, :selected, price: 10000, heading: heading1)
inv2 = create(:budget_investment, :selected, price: 20000, heading: heading2)
inv3 = create(:budget_investment, :selected, price: 40000, heading: heading1)
ballot = create(:budget_ballot, budget: budget)
ballot.investments << inv1 << inv2
expect(ballot.amount_spent(heading)).to eq 10000
expect(ballot.amount_spent(heading1)).to eq 10000
expect(ballot.amount_spent(heading2)).to eq 20000
ballot.investments << inv3
expect(ballot.amount_spent(heading)).to eq 50000
expect(ballot.amount_spent(heading1)).to eq 50000
expect(ballot.amount_spent(heading2)).to eq 20000
end
end
describe "#amount_available" do
it "returns how much is left after taking some investments" do
budget = create(:budget)
group = create(:budget_group, budget: budget)
heading1 = create(:budget_heading, group: group, price: 1000)
heading2 = create(:budget_heading, group: group, price: 300)
group1 = create(:budget_group, budget: budget)
group2 = create(:budget_group, budget: budget)
heading1 = create(:budget_heading, group: group1, price: 1000)
heading2 = create(:budget_heading, group: group2, price: 300)
inv1 = create(:budget_investment, :selected, price: 100, heading: heading1)
inv2 = create(:budget_investment, :selected, price: 200, heading: heading2)
inv3 = create(:budget_investment, :selected, price: 400, heading: heading1)