decidim-budgets 0.20.0 → 0.23.1.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +38 -3
  3. data/app/assets/images/decidim/budgets/icon.svg +1 -11
  4. data/app/assets/javascripts/decidim/budgets/projects.js.es6 +26 -4
  5. data/app/assets/stylesheets/decidim/budgets/budget/_budget-list.scss +152 -24
  6. data/app/cells/decidim/budgets/base_cell.rb +22 -0
  7. data/app/cells/decidim/budgets/budget_cell.rb +21 -0
  8. data/app/cells/decidim/budgets/budget_information_modal/show.erb +28 -0
  9. data/app/cells/decidim/budgets/budget_information_modal_cell.rb +22 -0
  10. data/app/cells/decidim/budgets/budget_list_item/show.erb +22 -0
  11. data/app/cells/decidim/budgets/budget_list_item_cell.rb +40 -0
  12. data/app/cells/decidim/budgets/budget_m/data.erb +12 -0
  13. data/app/cells/decidim/budgets/budget_m/footer.erb +5 -0
  14. data/app/cells/decidim/budgets/budget_m_cell.rb +16 -0
  15. data/app/cells/decidim/budgets/budgets_header/show.erb +7 -0
  16. data/app/cells/decidim/budgets/budgets_header_cell.rb +14 -0
  17. data/app/cells/decidim/budgets/budgets_list/card_list.erb +7 -0
  18. data/app/cells/decidim/budgets/budgets_list/highlighted.erb +11 -0
  19. data/app/cells/decidim/budgets/budgets_list/show.erb +20 -0
  20. data/app/cells/decidim/budgets/budgets_list/voted.erb +43 -0
  21. data/app/cells/decidim/budgets/budgets_list_cell.rb +39 -0
  22. data/app/cells/decidim/budgets/limit_announcement_cell.rb +52 -0
  23. data/app/cells/decidim/budgets/project_list_item/project_data.erb +19 -0
  24. data/app/cells/decidim/budgets/project_list_item/project_data_vote_button.erb +15 -0
  25. data/app/cells/decidim/budgets/project_list_item/project_image.erb +5 -0
  26. data/app/cells/decidim/budgets/project_list_item/project_text.erb +23 -0
  27. data/app/cells/decidim/budgets/project_list_item/show.erb +5 -0
  28. data/app/cells/decidim/budgets/project_list_item_cell.rb +66 -0
  29. data/app/cells/decidim/budgets/project_m/data.erb +3 -3
  30. data/app/cells/decidim/budgets/project_m/footer.erb +1 -1
  31. data/app/cells/decidim/budgets/project_m_cell.rb +4 -0
  32. data/app/cells/decidim/budgets/project_selected_status_cell.rb +28 -0
  33. data/app/cells/decidim/budgets/project_tags/show.erb +5 -0
  34. data/app/cells/decidim/budgets/project_tags_cell.rb +18 -0
  35. data/app/cells/decidim/budgets/project_voted_hint_cell.rb +33 -0
  36. data/app/cells/decidim/budgets/project_votes_count_cell.rb +44 -0
  37. data/app/commands/decidim/budgets/add_line_item.rb +7 -10
  38. data/app/commands/decidim/budgets/admin/create_budget.rb +48 -0
  39. data/app/commands/decidim/budgets/admin/create_project.rb +26 -11
  40. data/app/commands/decidim/budgets/admin/destroy_budget.rb +42 -0
  41. data/app/commands/decidim/budgets/admin/import_proposals_to_budgets.rb +26 -25
  42. data/app/commands/decidim/budgets/admin/update_budget.rb +48 -0
  43. data/app/commands/decidim/budgets/admin/update_project.rb +20 -2
  44. data/app/commands/decidim/budgets/checkout.rb +7 -6
  45. data/app/commands/decidim/budgets/remove_line_item.rb +3 -2
  46. data/app/controllers/concerns/decidim/budgets/needs_current_order.rb +18 -2
  47. data/app/controllers/concerns/decidim/budgets/orderable.rb +57 -0
  48. data/app/controllers/decidim/budgets/admin/application_controller.rb +10 -2
  49. data/app/controllers/decidim/budgets/admin/attachment_collections_controller.rb +1 -1
  50. data/app/controllers/decidim/budgets/admin/attachments_controller.rb +2 -2
  51. data/app/controllers/decidim/budgets/admin/budgets_controller.rb +82 -0
  52. data/app/controllers/decidim/budgets/admin/projects_controller.rb +15 -12
  53. data/app/controllers/decidim/budgets/admin/proposals_imports_controller.rb +10 -2
  54. data/app/controllers/decidim/budgets/application_controller.rb +23 -0
  55. data/app/controllers/decidim/budgets/budgets_controller.rb +24 -0
  56. data/app/controllers/decidim/budgets/line_items_controller.rb +10 -6
  57. data/app/controllers/decidim/budgets/orders_controller.rb +20 -6
  58. data/app/controllers/decidim/budgets/projects_controller.rb +28 -7
  59. data/app/forms/decidim/budgets/admin/budget_form.rb +39 -0
  60. data/app/forms/decidim/budgets/admin/component_form.rb +44 -0
  61. data/app/forms/decidim/budgets/admin/project_form.rb +25 -9
  62. data/app/forms/decidim/budgets/admin/project_import_proposals_form.rb +4 -0
  63. data/app/helpers/decidim/budgets/admin/application_helper.rb +13 -0
  64. data/app/helpers/decidim/budgets/application_helper.rb +11 -0
  65. data/app/helpers/decidim/budgets/projects_helper.rb +13 -0
  66. data/app/jobs/decidim/budgets/send_order_summary_job.rb +15 -0
  67. data/app/mailers/decidim/budgets/order_summary_mailer.rb +36 -0
  68. data/app/models/decidim/budgets/budget.rb +34 -0
  69. data/app/models/decidim/budgets/line_item.rb +4 -4
  70. data/app/models/decidim/budgets/order.rb +46 -16
  71. data/app/models/decidim/budgets/project.rb +37 -4
  72. data/app/permissions/decidim/budgets/admin/permissions.rb +23 -10
  73. data/app/permissions/decidim/budgets/permissions.rb +28 -6
  74. data/app/presenters/decidim/budgets/admin_log/budget_presenter.rb +42 -0
  75. data/app/queries/decidim/budgets/filtered_projects.rb +9 -1
  76. data/app/queries/decidim/budgets/metrics/budget_followers_metric_measure.rb +3 -2
  77. data/app/queries/decidim/budgets/metrics/budget_participants_metric_measure.rb +6 -5
  78. data/app/serializers/decidim/budgets/data_portability_budgets_order_serializer.rb +3 -2
  79. data/app/services/decidim/budgets/project_search.rb +13 -17
  80. data/app/types/decidim/budgets/budget_type.rb +24 -0
  81. data/app/types/decidim/budgets/budgets_type.rb +32 -0
  82. data/app/types/decidim/budgets/project_type.rb +26 -0
  83. data/app/views/decidim/budgets/admin/budgets/_form.html.erb +23 -0
  84. data/app/views/decidim/budgets/admin/budgets/edit.html.erb +7 -0
  85. data/app/views/decidim/budgets/admin/budgets/index.html.erb +58 -0
  86. data/app/views/decidim/budgets/admin/budgets/new.html.erb +7 -0
  87. data/app/views/decidim/budgets/admin/projects/_form.html.erb +15 -10
  88. data/app/views/decidim/budgets/admin/projects/edit.html.erb +2 -2
  89. data/app/views/decidim/budgets/admin/projects/index.html.erb +20 -7
  90. data/app/views/decidim/budgets/admin/projects/new.html.erb +2 -2
  91. data/app/views/decidim/budgets/admin/projects/proposals_picker.html.erb +1 -0
  92. data/app/views/decidim/budgets/admin/proposals_imports/new.html.erb +1 -1
  93. data/app/views/decidim/budgets/budgets/index.html.erb +5 -0
  94. data/app/views/decidim/budgets/order_summary_mailer/order_summary.html.erb +22 -0
  95. data/app/views/decidim/budgets/projects/_budget_confirm.html.erb +3 -3
  96. data/app/views/decidim/budgets/projects/_budget_summary.html.erb +26 -7
  97. data/app/views/decidim/budgets/projects/_filters.html.erb +15 -7
  98. data/app/views/decidim/budgets/projects/_linked_projects.html.erb +2 -2
  99. data/app/views/decidim/budgets/projects/_order_progress.html.erb +14 -18
  100. data/app/views/decidim/budgets/projects/_order_selected_projects.html.erb +3 -3
  101. data/app/views/decidim/budgets/projects/_project.html.erb +1 -57
  102. data/app/views/decidim/budgets/projects/_project_budget_button.html.erb +36 -7
  103. data/app/views/decidim/budgets/projects/_projects.html.erb +8 -1
  104. data/app/views/decidim/budgets/projects/index.html.erb +15 -7
  105. data/app/views/decidim/budgets/projects/index.js.erb +8 -0
  106. data/app/views/decidim/budgets/projects/show.html.erb +18 -11
  107. data/config/locales/am-ET.yml +1 -0
  108. data/config/locales/ar.yml +3 -17
  109. data/config/locales/bg-BG.yml +7 -0
  110. data/config/locales/bg.yml +6 -0
  111. data/config/locales/ca.yml +126 -12
  112. data/config/locales/cs.yml +145 -31
  113. data/config/locales/da-DK.yml +1 -0
  114. data/config/locales/da.yml +1 -0
  115. data/config/locales/de.yml +115 -12
  116. data/config/locales/el-GR.yml +1 -0
  117. data/config/locales/el.yml +169 -0
  118. data/config/locales/en.yml +126 -12
  119. data/config/locales/eo.yml +1 -0
  120. data/config/locales/es-MX.yml +126 -12
  121. data/config/locales/es-PY.yml +126 -12
  122. data/config/locales/es.yml +126 -12
  123. data/config/locales/et-EE.yml +1 -0
  124. data/config/locales/et.yml +1 -0
  125. data/config/locales/eu.yml +4 -14
  126. data/config/locales/fi-plain.yml +127 -13
  127. data/config/locales/fi.yml +138 -24
  128. data/config/locales/fr-CA.yml +266 -0
  129. data/config/locales/fr.yml +125 -12
  130. data/config/locales/ga-IE.yml +1 -0
  131. data/config/locales/gl.yml +3 -13
  132. data/config/locales/hr-HR.yml +1 -0
  133. data/config/locales/hr.yml +1 -0
  134. data/config/locales/hu.yml +19 -13
  135. data/config/locales/id-ID.yml +3 -12
  136. data/config/locales/is-IS.yml +108 -0
  137. data/config/locales/is.yml +114 -0
  138. data/config/locales/it.yml +97 -13
  139. data/config/locales/ja-JP.yml +172 -0
  140. data/config/locales/ja.yml +262 -0
  141. data/config/locales/ko-KR.yml +1 -0
  142. data/config/locales/ko.yml +1 -0
  143. data/config/locales/lt-LT.yml +1 -0
  144. data/config/locales/lt.yml +1 -0
  145. data/config/locales/lv.yml +164 -0
  146. data/config/locales/mt-MT.yml +1 -0
  147. data/config/locales/mt.yml +1 -0
  148. data/config/locales/nl.yml +117 -17
  149. data/config/locales/no.yml +129 -8
  150. data/config/locales/om-ET.yml +1 -0
  151. data/config/locales/pl.yml +175 -62
  152. data/config/locales/pt-BR.yml +4 -14
  153. data/config/locales/pt.yml +63 -47
  154. data/config/locales/ro-RO.yml +172 -0
  155. data/config/locales/ru.yml +3 -15
  156. data/config/locales/sk-SK.yml +180 -0
  157. data/config/locales/sk.yml +166 -0
  158. data/config/locales/sl.yml +1 -0
  159. data/config/locales/so-SO.yml +1 -0
  160. data/config/locales/sr-CS.yml +27 -0
  161. data/config/locales/sv.yml +105 -12
  162. data/config/locales/ti-ER.yml +1 -0
  163. data/config/locales/tr-TR.yml +3 -13
  164. data/config/locales/uk.yml +3 -15
  165. data/config/locales/vi-VN.yml +1 -0
  166. data/config/locales/vi.yml +1 -0
  167. data/config/locales/zh-CN.yml +260 -0
  168. data/config/locales/zh-TW.yml +1 -0
  169. data/db/migrate/20200617105120_create_decidim_budgets.rb +15 -0
  170. data/db/migrate/20200629072626_rename_budget_to_budget_ammount.rb +7 -0
  171. data/db/migrate/20200629134013_add_budget_reference_to_project.rb +7 -0
  172. data/db/migrate/20200706142609_add_budget_reference_to_order.rb +7 -0
  173. data/db/migrate/20200714103519_move_budgets_to_own_model.rb +109 -0
  174. data/db/migrate/20200717140012_add_scope_to_budgets.rb +7 -0
  175. data/db/migrate/20200728075039_add_selected_at_to_project.rb +7 -0
  176. data/db/migrate/20200804175222_votes_enabled_to_votes_choices.rb +35 -0
  177. data/db/migrate/20200827154129_add_commentable_counter_cache_to_projects.rb +9 -0
  178. data/lib/decidim/budgets.rb +1 -0
  179. data/lib/decidim/budgets/admin_engine.rb +12 -5
  180. data/lib/decidim/budgets/component.rb +94 -38
  181. data/lib/decidim/budgets/engine.rb +8 -6
  182. data/lib/decidim/budgets/seeds/city.jpeg +0 -0
  183. data/lib/decidim/budgets/test/factories.rb +71 -9
  184. data/lib/decidim/budgets/version.rb +1 -1
  185. data/lib/decidim/budgets/workflows.rb +17 -0
  186. data/lib/decidim/budgets/workflows/all.rb +20 -0
  187. data/lib/decidim/budgets/workflows/base.rb +132 -0
  188. data/lib/decidim/budgets/workflows/one.rb +33 -0
  189. metadata +124 -17
@@ -8,6 +8,29 @@ module Decidim
8
8
  # Note that it inherits from `Decidim::Components::BaseController`, which
9
9
  # override its layout and provide all kinds of useful methods.
10
10
  class ApplicationController < Decidim::Components::BaseController
11
+ helper_method :current_workflow, :voting_finished?, :voting_open?
12
+
13
+ def current_workflow
14
+ @current_workflow ||= Decidim::Budgets.workflows[workflow_name].new(current_component, current_user)
15
+ end
16
+
17
+ def voting_open?
18
+ current_settings.votes == "enabled"
19
+ end
20
+
21
+ def voting_finished?
22
+ current_settings.votes == "finished"
23
+ end
24
+
25
+ def show_votes_count?
26
+ current_settings.show_votes?
27
+ end
28
+
29
+ private
30
+
31
+ def workflow_name
32
+ @workflow_name ||= current_component.settings.workflow.to_sym
33
+ end
11
34
  end
12
35
  end
13
36
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ # Exposes the budget resources so users can participate on them
6
+ class BudgetsController < Decidim::Budgets::ApplicationController
7
+ def index
8
+ redirect_to budget_projects_path(current_workflow.single) if current_workflow.single?
9
+ end
10
+
11
+ def show
12
+ raise ActionController::RoutingError, "Not Found" unless budget
13
+
14
+ redirect_to budget_projects_path(budget)
15
+ end
16
+
17
+ private
18
+
19
+ def budget
20
+ @budget ||= Budget.where(component: current_component).includes(:projects).find_by(id: params[:id])
21
+ end
22
+ end
23
+ end
24
+ end
@@ -6,16 +6,16 @@ module Decidim
6
6
  class LineItemsController < Decidim::Budgets::ApplicationController
7
7
  include NeedsCurrentOrder
8
8
 
9
- helper_method :project
9
+ helper_method :budget, :project
10
10
 
11
11
  def create
12
- enforce_permission_to :vote, :project, project: project
12
+ enforce_permission_to :vote, :project, project: project, budget: budget, workflow: current_workflow
13
13
 
14
14
  respond_to do |format|
15
- AddLineItem.call(current_order, project, current_user) do
15
+ AddLineItem.call(persisted_current_order, project, current_user) do
16
16
  on(:ok) do |order|
17
17
  self.current_order = order
18
- format.html { redirect_to :back }
18
+ format.html { redirect_back(fallback_location: budget_path(budget)) }
19
19
  format.js { render "update_budget" }
20
20
  end
21
21
 
@@ -30,7 +30,7 @@ module Decidim
30
30
  respond_to do |format|
31
31
  RemoveLineItem.call(current_order, project) do
32
32
  on(:ok) do |_order|
33
- format.html { redirect_to :back }
33
+ format.html { redirect_back(fallback_location: budget_path(budget)) }
34
34
  format.js { render "update_budget" }
35
35
  end
36
36
 
@@ -44,7 +44,11 @@ module Decidim
44
44
  private
45
45
 
46
46
  def project
47
- @project ||= Project.find_by(id: params[:project_id], component: current_component)
47
+ @project ||= Project.includes(:budget).find_by(id: params[:project_id], decidim_budgets_budget_id: params[:budget_id])
48
+ end
49
+
50
+ def budget
51
+ @budget ||= project.budget
48
52
  end
49
53
  end
50
54
  end
@@ -7,17 +7,17 @@ module Decidim
7
7
  include NeedsCurrentOrder
8
8
 
9
9
  def checkout
10
- enforce_permission_to :vote, :project, order: current_order
10
+ enforce_permission_to :vote, :project, order: current_order, budget: budget, workflow: current_workflow
11
11
 
12
- Checkout.call(current_order, current_component) do
12
+ Checkout.call(current_order) do
13
13
  on(:ok) do
14
14
  flash[:notice] = I18n.t("orders.checkout.success", scope: "decidim")
15
- redirect_to projects_path
15
+ redirect_to budgets_path
16
16
  end
17
17
 
18
18
  on(:invalid) do
19
19
  flash.now[:alert] = I18n.t("orders.checkout.error", scope: "decidim")
20
- redirect_to projects_path
20
+ redirect_to budgets_path
21
21
  end
22
22
  end
23
23
  end
@@ -26,15 +26,29 @@ module Decidim
26
26
  CancelOrder.call(current_order) do
27
27
  on(:ok) do
28
28
  flash[:notice] = I18n.t("orders.destroy.success", scope: "decidim")
29
- redirect_to projects_path
29
+ redirect_to redirect_path
30
30
  end
31
31
 
32
32
  on(:invalid) do
33
33
  flash.now[:alert] = I18n.t("orders.destroy.error", scope: "decidim")
34
- redirect_to projects_path
34
+ redirect_to redirect_path
35
35
  end
36
36
  end
37
37
  end
38
+
39
+ private
40
+
41
+ def budget
42
+ @budget ||= Budget.find_by(id: params[:budget_id])
43
+ end
44
+
45
+ def redirect_path
46
+ if params.dig(:return_to) == "budget"
47
+ budget_path(budget)
48
+ else
49
+ budgets_path
50
+ end
51
+ end
38
52
  end
39
53
  end
40
54
  end
@@ -6,18 +6,34 @@ module Decidim
6
6
  class ProjectsController < Decidim::Budgets::ApplicationController
7
7
  include FilterResource
8
8
  include NeedsCurrentOrder
9
- include Orderable
9
+ include Decidim::Budgets::Orderable
10
10
 
11
- helper_method :projects, :project
11
+ helper_method :projects, :project, :budget
12
+
13
+ def index
14
+ raise ActionController::RoutingError, "Not Found" unless budget
15
+ end
16
+
17
+ def show
18
+ raise ActionController::RoutingError, "Not Found" unless budget
19
+ raise ActionController::RoutingError, "Not Found" unless project
20
+ end
12
21
 
13
22
  private
14
23
 
24
+ def budget
25
+ @budget ||= Budget.where(component: current_component).includes(:projects).find_by(id: params[:budget_id])
26
+ end
27
+
15
28
  def projects
16
- @projects ||= search.results.order_randomly(random_seed).page(params[:page]).per(current_component.settings.projects_per_page)
29
+ return @projects if @projects
30
+
31
+ @projects = search.results.page(params[:page]).per(current_component.settings.projects_per_page)
32
+ @projects = reorder(@projects)
17
33
  end
18
34
 
19
35
  def project
20
- @project ||= search.results.find(params[:id])
36
+ @project ||= Project.find_by(id: params[:id])
21
37
  end
22
38
 
23
39
  def search_klass
@@ -27,13 +43,18 @@ module Decidim
27
43
  def default_filter_params
28
44
  {
29
45
  search_text: "",
30
- scope_id: "",
31
- category_id: ""
46
+ status: default_filter_status_params,
47
+ scope_id: default_filter_scope_params,
48
+ category_id: default_filter_category_params
32
49
  }
33
50
  end
34
51
 
52
+ def default_filter_status_params
53
+ voting_finished? ? %w(selected) : %w(all)
54
+ end
55
+
35
56
  def context_params
36
- { component: current_component, organization: current_organization }
57
+ { budget: budget, component: current_component, organization: current_organization }
37
58
  end
38
59
  end
39
60
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ module Admin
6
+ # This class holds a Form to create/update budgets from Decidim's admin panel.
7
+ class BudgetForm < Decidim::Form
8
+ include TranslatableAttributes
9
+
10
+ mimic :budget
11
+
12
+ translatable_attribute :title, String
13
+ attribute :weight, Integer, default: 0
14
+ translatable_attribute :description, String
15
+ attribute :total_budget, Integer, default: 0
16
+ attribute :decidim_scope_id, Integer
17
+
18
+ validates :title, translatable_presence: true
19
+ validates :weight, :total_budget, numericality: { greater_than_or_equal_to: 0 }
20
+ validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
21
+ validates :decidim_scope_id, scope_belongs_to_component: true, if: ->(form) { form.decidim_scope_id.present? }
22
+
23
+ # Finds the Scope from the given decidim_scope_id, uses the compoenent scope if missing.
24
+ #
25
+ # Returns a Decidim::Scope
26
+ def scope
27
+ @scope ||= @decidim_scope_id ? current_component.scopes.find_by(id: @decidim_scope_id) : current_component.scope
28
+ end
29
+
30
+ # Scope identifier
31
+ #
32
+ # Returns the scope identifier related to the meeting
33
+ def decidim_scope_id
34
+ @decidim_scope_id || scope&.id
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ module Admin
6
+ # A form object for the budgets component. Used to attach the component
7
+ # to a participatory process from the admin panel.
8
+ #
9
+ class ComponentForm < Decidim::Admin::ComponentForm
10
+ validate :budget_voting_rule_enabled_setting, :budget_voting_rule_value_setting
11
+
12
+ private
13
+
14
+ # Validations on budget settings:
15
+ # - a voting rule must be enabled.
16
+ def budget_voting_rule_enabled_setting
17
+ return unless manifest&.name == :budgets
18
+
19
+ i18n_error_scope = "decidim.components.budgets.settings.global.form.errors"
20
+ if settings.vote_rule_threshold_percent_enabled.blank? && settings.vote_rule_minimum_budget_projects_enabled.blank?
21
+ settings.errors.add(:vote_rule_threshold_percent_enabled, I18n.t(:budget_voting_rule_required, scope: i18n_error_scope))
22
+ settings.errors.add(:vote_rule_minimum_budget_projects_enabled, I18n.t(:budget_voting_rule_required, scope: i18n_error_scope))
23
+ end
24
+
25
+ if settings.vote_rule_threshold_percent_enabled && settings.vote_rule_minimum_budget_projects_enabled
26
+ settings.errors.add(:vote_rule_threshold_percent_enabled, I18n.t(:budget_voting_rule_only_one, scope: i18n_error_scope))
27
+ settings.errors.add(:vote_rule_minimum_budget_projects_enabled, I18n.t(:budget_voting_rule_only_one, scope: i18n_error_scope))
28
+ end
29
+ end
30
+
31
+ # - the value must be a valid number
32
+ def budget_voting_rule_value_setting
33
+ return unless manifest&.name == :budgets
34
+
35
+ invalid_percent_number = settings.vote_threshold_percent.blank? || settings.vote_threshold_percent.to_i.negative?
36
+ settings.errors.add(:vote_threshold_percent) if settings.vote_rule_threshold_percent_enabled && invalid_percent_number
37
+
38
+ invalid_minimum_number = settings.vote_minimum_budget_projects_number.blank? || (settings.vote_minimum_budget_projects_number.to_i < 1)
39
+ settings.errors.add(:vote_minimum_budget_projects_number) if settings.vote_rule_minimum_budget_projects_enabled && invalid_minimum_number
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -12,24 +12,30 @@ module Decidim
12
12
  translatable_attribute :title, String
13
13
  translatable_attribute :description, String
14
14
 
15
- attribute :budget, Integer
15
+ attribute :budget_amount, Integer
16
16
  attribute :decidim_scope_id, Integer
17
17
  attribute :decidim_category_id, Integer
18
18
  attribute :proposal_ids, Array[Integer]
19
+ attribute :attachment, AttachmentForm
20
+ attribute :photos, Array[String]
21
+ attribute :add_photos, Array
22
+ attribute :selected, Boolean
19
23
 
20
24
  validates :title, translatable_presence: true
21
25
  validates :description, translatable_presence: true
22
- validates :budget, presence: true, numericality: { greater_than: 0 }
26
+ validates :budget_amount, presence: true, numericality: { greater_than: 0 }
23
27
 
24
28
  validates :category, presence: true, if: ->(form) { form.decidim_category_id.present? }
25
29
  validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
30
+ validates :decidim_scope_id, scope_belongs_to_component: true, if: ->(form) { form.decidim_scope_id.present? }
26
31
 
27
- validate :scope_belongs_to_participatory_space_scope
32
+ validate :notify_missing_attachment_if_errored
28
33
 
29
34
  delegate :categories, to: :current_component
30
35
 
31
36
  def map_model(model)
32
37
  self.proposal_ids = model.linked_resources(:proposals, "included_proposals").pluck(:id)
38
+ self.selected = model.selected?
33
39
 
34
40
  return unless model.categorization
35
41
 
@@ -38,9 +44,15 @@ module Decidim
38
44
 
39
45
  def proposals
40
46
  @proposals ||= Decidim.find_resource_manifest(:proposals).try(:resource_scope, current_component)
41
- &.published
47
+ &.where(id: proposal_ids)
42
48
  &.order(title: :asc)
43
- &.map { |proposal| [present(proposal).title, proposal.id] }
49
+ end
50
+
51
+ # Finds the Budget from the decidim_budgets_budget_id.
52
+ #
53
+ # Returns a Decidim::Budgets:Budget
54
+ def budget
55
+ @budget ||= context[:budget]
44
56
  end
45
57
 
46
58
  # Finds the Category from the decidim_category_id.
@@ -50,11 +62,11 @@ module Decidim
50
62
  @category ||= categories.find_by(id: decidim_category_id)
51
63
  end
52
64
 
53
- # Finds the Scope from the given decidim_scope_id, uses participatory space scope if missing.
65
+ # Finds the Scope from the given decidim_scope_id, uses the component scope if missing.
54
66
  #
55
67
  # Returns a Decidim::Scope
56
68
  def scope
57
- @scope ||= @decidim_scope_id ? current_participatory_space.scopes.find_by(id: @decidim_scope_id) : current_participatory_space.scope
69
+ @scope ||= @decidim_scope_id ? current_component.scopes.find_by(id: @decidim_scope_id) : current_component.scope
58
70
  end
59
71
 
60
72
  # Scope identifier
@@ -66,8 +78,12 @@ module Decidim
66
78
 
67
79
  private
68
80
 
69
- def scope_belongs_to_participatory_space_scope
70
- errors.add(:decidim_scope_id, :invalid) if current_participatory_space.out_of_scope?(scope)
81
+ # This method will add an error to the `attachment` field only if there's
82
+ # any error in any other field. This is needed because when the form has
83
+ # an error, the attachment is lost, so we need a way to inform the user of
84
+ # this problem.
85
+ def notify_missing_attachment_if_errored
86
+ errors.add(:add_photos, :needs_to_be_reattached) if errors.any? && add_photos.present?
71
87
  end
72
88
  end
73
89
  end
@@ -29,6 +29,10 @@ module Decidim
29
29
  [component.name[I18n.locale.to_s], component.id]
30
30
  end
31
31
  end
32
+
33
+ def budget
34
+ @budget ||= context[:budget]
35
+ end
32
36
  end
33
37
  end
34
38
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ module Admin
6
+ # Custom helpers, scoped to the budgets admin engine.
7
+ #
8
+ module ApplicationHelper
9
+ include Decidim::Admin::ResourceScopeHelper
10
+ end
11
+ end
12
+ end
13
+ end
@@ -8,6 +8,17 @@ module Decidim
8
8
  include PaginateHelper
9
9
  include Decidim::Comments::CommentsHelper
10
10
  include ProjectsHelper
11
+ include Decidim::CheckBoxesTreeHelper
12
+
13
+ def filter_status_values
14
+ TreeNode.new(
15
+ TreePoint.new("", t("decidim.budgets.projects.filters.status_values.all")),
16
+ [
17
+ TreePoint.new("selected", t("decidim.budgets.projects.filters.status_values.selected")),
18
+ TreePoint.new("not_selected", t("decidim.budgets.projects.filters.status_values.not_selected"))
19
+ ]
20
+ )
21
+ end
11
22
  end
12
23
  end
13
24
  end
@@ -16,6 +16,19 @@ module Decidim
16
16
  current_order&.budget_percent.to_f.floor
17
17
  end
18
18
 
19
+ # Return the minimum percentage of the current order budget from the total budget
20
+ def current_order_budget_percent_minimum
21
+ return 0 if current_order.minimum_projects_rule?
22
+
23
+ component_settings.vote_threshold_percent
24
+ end
25
+
26
+ def budget_confirm_disabled_attr
27
+ return if current_order_can_be_checked_out?
28
+
29
+ %( disabled="disabled" ).html_safe
30
+ end
31
+
19
32
  # Return true if the current order is checked out
20
33
  delegate :checked_out?, to: :current_order, prefix: true, allow_nil: true
21
34
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ class SendOrderSummaryJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(order)
9
+ return if order&.user&.email.blank?
10
+
11
+ OrderSummaryMailer.order_summary(order).deliver_now
12
+ end
13
+ end
14
+ end
15
+ end