decidim-budgets 0.21.0 → 0.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) 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 +24 -19
  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 +10 -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 +47 -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 +12 -24
  80. data/app/types/decidim/budgets/budget_type.rb +24 -0
  81. data/app/types/decidim/budgets/budgets_type.rb +6 -6
  82. data/app/types/decidim/budgets/project_type.rb +2 -1
  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 +22 -13
  107. data/config/locales/am-ET.yml +1 -0
  108. data/config/locales/ar.yml +2 -16
  109. data/config/locales/bg-BG.yml +7 -0
  110. data/config/locales/bg.yml +6 -0
  111. data/config/locales/ca.yml +125 -11
  112. data/config/locales/cs.yml +148 -34
  113. data/config/locales/da-DK.yml +1 -0
  114. data/config/locales/da.yml +1 -0
  115. data/config/locales/de.yml +143 -29
  116. data/config/locales/el.yml +168 -0
  117. data/config/locales/en.yml +125 -11
  118. data/config/locales/eo.yml +1 -0
  119. data/config/locales/es-MX.yml +125 -11
  120. data/config/locales/es-PY.yml +125 -11
  121. data/config/locales/es.yml +125 -11
  122. data/config/locales/et-EE.yml +1 -0
  123. data/config/locales/et.yml +1 -0
  124. data/config/locales/eu.yml +5 -13
  125. data/config/locales/fi-plain.yml +126 -12
  126. data/config/locales/fi.yml +136 -22
  127. data/config/locales/fr-CA.yml +267 -0
  128. data/config/locales/fr.yml +125 -11
  129. data/config/locales/ga-IE.yml +1 -0
  130. data/config/locales/gl.yml +70 -15
  131. data/config/locales/hr-HR.yml +1 -0
  132. data/config/locales/hr.yml +1 -0
  133. data/config/locales/hu.yml +18 -12
  134. data/config/locales/id-ID.yml +2 -11
  135. data/config/locales/is-IS.yml +0 -11
  136. data/config/locales/is.yml +114 -0
  137. data/config/locales/it.yml +96 -12
  138. data/config/locales/ja-JP.yml +172 -0
  139. data/config/locales/ja.yml +262 -0
  140. data/config/locales/ko-KR.yml +1 -0
  141. data/config/locales/ko.yml +1 -0
  142. data/config/locales/lt-LT.yml +1 -0
  143. data/config/locales/lt.yml +1 -0
  144. data/config/locales/lv.yml +164 -0
  145. data/config/locales/mt-MT.yml +1 -0
  146. data/config/locales/mt.yml +1 -0
  147. data/config/locales/nl.yml +112 -12
  148. data/config/locales/no.yml +42 -13
  149. data/config/locales/om-ET.yml +1 -0
  150. data/config/locales/pl.yml +175 -61
  151. data/config/locales/pt-BR.yml +3 -13
  152. data/config/locales/pt.yml +62 -46
  153. data/config/locales/ro-RO.yml +172 -0
  154. data/config/locales/ru.yml +2 -14
  155. data/config/locales/si-LK.yml +1 -0
  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 +125 -11
  162. data/config/locales/sw-KE.yml +1 -0
  163. data/config/locales/ti-ER.yml +1 -0
  164. data/config/locales/tr-TR.yml +152 -38
  165. data/config/locales/uk.yml +2 -14
  166. data/config/locales/vi-VN.yml +1 -0
  167. data/config/locales/vi.yml +1 -0
  168. data/config/locales/zh-CN.yml +260 -0
  169. data/config/locales/zh-TW.yml +1 -0
  170. data/db/migrate/20200617105120_create_decidim_budgets.rb +15 -0
  171. data/db/migrate/20200629072626_rename_budget_to_budget_ammount.rb +7 -0
  172. data/db/migrate/20200629134013_add_budget_reference_to_project.rb +7 -0
  173. data/db/migrate/20200706142609_add_budget_reference_to_order.rb +7 -0
  174. data/db/migrate/20200714103519_move_budgets_to_own_model.rb +109 -0
  175. data/db/migrate/20200717140012_add_scope_to_budgets.rb +7 -0
  176. data/db/migrate/20200728075039_add_selected_at_to_project.rb +7 -0
  177. data/db/migrate/20200804175222_votes_enabled_to_votes_choices.rb +35 -0
  178. data/db/migrate/20200827154129_add_commentable_counter_cache_to_projects.rb +9 -0
  179. data/lib/decidim/budgets.rb +1 -0
  180. data/lib/decidim/budgets/admin_engine.rb +12 -5
  181. data/lib/decidim/budgets/component.rb +87 -38
  182. data/lib/decidim/budgets/engine.rb +8 -6
  183. data/lib/decidim/budgets/seeds/city.jpeg +0 -0
  184. data/lib/decidim/budgets/test/factories.rb +71 -9
  185. data/lib/decidim/budgets/version.rb +1 -1
  186. data/lib/decidim/budgets/workflows.rb +17 -0
  187. data/lib/decidim/budgets/workflows/all.rb +20 -0
  188. data/lib/decidim/budgets/workflows/base.rb +132 -0
  189. data/lib/decidim/budgets/workflows/one.rb +33 -0
  190. metadata +123 -19
@@ -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,29 +43,18 @@ module Decidim
27
43
  def default_filter_params
28
44
  {
29
45
  search_text: "",
46
+ status: default_filter_status_params,
30
47
  scope_id: default_filter_scope_params,
31
48
  category_id: default_filter_category_params
32
49
  }
33
50
  end
34
51
 
35
- def default_filter_category_params
36
- return "all" unless current_component.participatory_space.categories.any?
37
-
38
- ["all"] + current_component.participatory_space.categories.map { |category| category.id.to_s }
39
- end
40
-
41
- def default_filter_scope_params
42
- return "all" unless current_component.participatory_space.scopes.any?
43
-
44
- if current_component.participatory_space.scope
45
- ["all", current_component.participatory_space.scope.id] + current_component.participatory_space.scope.children.map { |scope| scope.id.to_s }
46
- else
47
- %w(all global) + current_component.participatory_space.scopes.map { |scope| scope.id.to_s }
48
- end
52
+ def default_filter_status_params
53
+ voting_finished? ? %w(selected) : %w(all)
49
54
  end
50
55
 
51
56
  def context_params
52
- { component: current_component, organization: current_organization }
57
+ { budget: budget, component: current_component, organization: current_organization }
53
58
  end
54
59
  end
55
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
@@ -9,6 +9,16 @@ module Decidim
9
9
  include Decidim::Comments::CommentsHelper
10
10
  include ProjectsHelper
11
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
12
22
  end
13
23
  end
14
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