decidim-budgets 0.20.1 → 0.23.1

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 (188) 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.yml +169 -0
  117. data/config/locales/en.yml +126 -12
  118. data/config/locales/eo.yml +1 -0
  119. data/config/locales/es-MX.yml +126 -12
  120. data/config/locales/es-PY.yml +126 -12
  121. data/config/locales/es.yml +126 -12
  122. data/config/locales/et-EE.yml +1 -0
  123. data/config/locales/et.yml +1 -0
  124. data/config/locales/eu.yml +4 -14
  125. data/config/locales/fi-plain.yml +127 -13
  126. data/config/locales/fi.yml +137 -23
  127. data/config/locales/fr-CA.yml +266 -0
  128. data/config/locales/fr.yml +125 -12
  129. data/config/locales/ga-IE.yml +1 -0
  130. data/config/locales/gl.yml +3 -13
  131. data/config/locales/hr-HR.yml +1 -0
  132. data/config/locales/hr.yml +1 -0
  133. data/config/locales/hu.yml +19 -13
  134. data/config/locales/id-ID.yml +3 -12
  135. data/config/locales/is-IS.yml +1 -12
  136. data/config/locales/is.yml +114 -0
  137. data/config/locales/it.yml +97 -13
  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 +113 -13
  148. data/config/locales/no.yml +39 -13
  149. data/config/locales/om-ET.yml +1 -0
  150. data/config/locales/pl.yml +175 -62
  151. data/config/locales/pt-BR.yml +4 -14
  152. data/config/locales/pt.yml +63 -47
  153. data/config/locales/ro-RO.yml +172 -0
  154. data/config/locales/ru.yml +3 -15
  155. data/config/locales/sk-SK.yml +180 -0
  156. data/config/locales/sk.yml +166 -0
  157. data/config/locales/sl.yml +1 -0
  158. data/config/locales/so-SO.yml +1 -0
  159. data/config/locales/sr-CS.yml +27 -0
  160. data/config/locales/sv.yml +126 -12
  161. data/config/locales/ti-ER.yml +1 -0
  162. data/config/locales/tr-TR.yml +3 -13
  163. data/config/locales/uk.yml +3 -15
  164. data/config/locales/vi-VN.yml +1 -0
  165. data/config/locales/vi.yml +1 -0
  166. data/config/locales/zh-CN.yml +260 -0
  167. data/config/locales/zh-TW.yml +1 -0
  168. data/db/migrate/20200617105120_create_decidim_budgets.rb +15 -0
  169. data/db/migrate/20200629072626_rename_budget_to_budget_ammount.rb +7 -0
  170. data/db/migrate/20200629134013_add_budget_reference_to_project.rb +7 -0
  171. data/db/migrate/20200706142609_add_budget_reference_to_order.rb +7 -0
  172. data/db/migrate/20200714103519_move_budgets_to_own_model.rb +109 -0
  173. data/db/migrate/20200717140012_add_scope_to_budgets.rb +7 -0
  174. data/db/migrate/20200728075039_add_selected_at_to_project.rb +7 -0
  175. data/db/migrate/20200804175222_votes_enabled_to_votes_choices.rb +35 -0
  176. data/db/migrate/20200827154129_add_commentable_counter_cache_to_projects.rb +9 -0
  177. data/lib/decidim/budgets.rb +1 -0
  178. data/lib/decidim/budgets/admin_engine.rb +12 -5
  179. data/lib/decidim/budgets/component.rb +94 -38
  180. data/lib/decidim/budgets/engine.rb +8 -6
  181. data/lib/decidim/budgets/seeds/city.jpeg +0 -0
  182. data/lib/decidim/budgets/test/factories.rb +71 -9
  183. data/lib/decidim/budgets/version.rb +1 -1
  184. data/lib/decidim/budgets/workflows.rb +17 -0
  185. data/lib/decidim/budgets/workflows/all.rb +20 -0
  186. data/lib/decidim/budgets/workflows/base.rb +132 -0
  187. data/lib/decidim/budgets/workflows/one.rb +33 -0
  188. metadata +121 -16
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ class OrderSummaryMailer < Decidim::ApplicationMailer
6
+ include Decidim::TranslationsHelper
7
+ include Decidim::SanitizeHelper
8
+
9
+ helper Decidim::TranslationsHelper
10
+
11
+ # Send an email to an user with the summary of the order.
12
+ #
13
+ # order - the order that was just created
14
+ def order_summary(order)
15
+ user = order.user
16
+
17
+ with_user(user) do
18
+ @user = user
19
+ @order = order
20
+ @budget = order.budget
21
+ @space = order.budget.participatory_space
22
+ @component = order.budget.component
23
+ @organization = order.budget.participatory_space.organization
24
+
25
+ subject = I18n.t(
26
+ "order_summary.subject",
27
+ scope: "decidim.budgets.order_summary_mailer",
28
+ budget_name: translated_attribute(@budget.title),
29
+ space_name: translated_attribute(@space.title)
30
+ )
31
+ mail(to: user.email, subject: subject)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ # The data store for a budget in the Decidim::Budgets component.
6
+ class Budget < ApplicationRecord
7
+ include Decidim::Resourceable
8
+ include Decidim::ScopableResource
9
+ include Decidim::HasComponent
10
+ include Decidim::Searchable
11
+ include Traceable
12
+ include Loggable
13
+
14
+ component_manifest_name "budgets"
15
+
16
+ has_many :projects, foreign_key: "decidim_budgets_budget_id", class_name: "Decidim::Budgets::Project", inverse_of: :budget, dependent: :destroy
17
+ has_many :orders, foreign_key: "decidim_budgets_budget_id", class_name: "Decidim::Budgets::Order", inverse_of: :budget, dependent: :destroy
18
+
19
+ delegate :participatory_space, :manifest, :settings, to: :component
20
+
21
+ searchable_fields({
22
+ participatory_space: { component: :participatory_space },
23
+ A: :title,
24
+ D: [:description, :total_budget]
25
+ },
26
+ index_on_create: ->(budget) { budget.visible? },
27
+ index_on_update: ->(budget) { budget.visible? })
28
+
29
+ def self.log_presenter_class_for(_log)
30
+ Decidim::Budgets::AdminLog::BudgetPresenter
31
+ end
32
+ end
33
+ end
34
+ end
@@ -2,19 +2,19 @@
2
2
 
3
3
  module Decidim
4
4
  module Budgets
5
- # The data store for a LineItem in the Decidim::Budgets component. It describes an
5
+ # The data store for a LineItem in the Budget resource. It describes an
6
6
  # association between an order and a project.
7
7
  class LineItem < Budgets::ApplicationRecord
8
8
  belongs_to :order, class_name: "Decidim::Budgets::Order", foreign_key: "decidim_order_id"
9
9
  belongs_to :project, class_name: "Decidim::Budgets::Project", foreign_key: "decidim_project_id"
10
10
 
11
11
  validates :order, uniqueness: { scope: :project }
12
- validate :same_component
12
+ validate :same_budget
13
13
 
14
- def same_component
14
+ def same_budget
15
15
  return unless order && project
16
16
 
17
- errors.add(:order, :invalid) unless order.component == project.component
17
+ errors.add(:order, :invalid) unless order.budget == project.budget
18
18
  end
19
19
  end
20
20
  end
@@ -5,18 +5,17 @@ module Decidim
5
5
  # The data store for a Order in the Decidim::Budgets component. It is unique for each
6
6
  # user and component and contains a collection of projects
7
7
  class Order < Budgets::ApplicationRecord
8
- include Decidim::HasComponent
9
8
  include Decidim::DataPortability
10
9
  include Decidim::NewsletterParticipant
11
10
 
12
- component_manifest_name "budgets"
13
-
14
11
  belongs_to :user, class_name: "Decidim::User", foreign_key: "decidim_user_id"
15
-
12
+ belongs_to :budget, foreign_key: "decidim_budgets_budget_id", class_name: "Decidim::Budgets::Budget", inverse_of: :orders
13
+ has_one :component, through: :budget, foreign_key: "decidim_component_id", class_name: "Decidim::Component"
16
14
  has_many :line_items, class_name: "Decidim::Budgets::LineItem", foreign_key: "decidim_order_id", dependent: :destroy
17
15
  has_many :projects, through: :line_items, class_name: "Decidim::Budgets::Project", foreign_key: "decidim_project_id"
18
16
 
19
- validates :user, uniqueness: { scope: :component }
17
+ validates :user, uniqueness: { scope: :budget }
18
+ validates :budget, presence: true
20
19
  validate :user_belongs_to_organization
21
20
 
22
21
  validates :total_budget, numericality: {
@@ -27,12 +26,14 @@ module Decidim
27
26
  less_than_or_equal_to: :maximum_budget
28
27
  }
29
28
 
29
+ validate :reach_minimum_projects, if: :checked_out?
30
+
30
31
  scope :finished, -> { where.not(checked_out_at: nil) }
31
32
  scope :pending, -> { where(checked_out_at: nil) }
32
33
 
33
34
  # Public: Returns the sum of project budgets
34
35
  def total_budget
35
- projects.to_a.sum(&:budget)
36
+ projects.to_a.sum(&:budget_amount)
36
37
  end
37
38
 
38
39
  # Public: Returns true if the order has been checked out
@@ -42,26 +43,45 @@ module Decidim
42
43
 
43
44
  # Public: Check if the order total budget is enough to checkout
44
45
  def can_checkout?
45
- total_budget.to_f >= minimum_budget
46
+ if minimum_projects_rule?
47
+ projects.count >= minimum_projects
48
+ else
49
+ total_budget.to_f >= minimum_budget
50
+ end
46
51
  end
47
52
 
48
53
  # Public: Returns the order budget percent from the settings total budget
49
54
  def budget_percent
50
- (total_budget.to_f / component.settings.total_budget.to_f) * 100
55
+ (total_budget.to_f / budget.total_budget.to_f) * 100
51
56
  end
52
57
 
53
58
  # Public: Returns the required minimum budget to checkout
54
59
  def minimum_budget
55
- return 0 unless component
60
+ return 0 unless budget
61
+ return 0 if minimum_projects_rule?
56
62
 
57
- component.settings.total_budget.to_f * (component.settings.vote_threshold_percent.to_f / 100)
63
+ budget.total_budget.to_f * (budget.settings.vote_threshold_percent.to_f / 100)
58
64
  end
59
65
 
60
66
  # Public: Returns the required maximum budget to checkout
61
67
  def maximum_budget
62
- return 0 unless component
68
+ return 0 unless budget
69
+
70
+ budget.total_budget.to_f
71
+ end
72
+
73
+ # Public: Returns if it is required a minimum projects limit to checkout
74
+ def minimum_projects_rule?
75
+ return unless budget
63
76
 
64
- component.settings.total_budget.to_f
77
+ budget.settings.vote_rule_minimum_budget_projects_enabled
78
+ end
79
+
80
+ # Public: Returns the required minimum projects to checkout
81
+ def minimum_projects
82
+ return 0 unless budget
83
+
84
+ budget.settings.vote_minimum_budget_projects_number
65
85
  end
66
86
 
67
87
  def self.user_collection(user)
@@ -73,20 +93,30 @@ module Decidim
73
93
  end
74
94
 
75
95
  def self.newsletter_participant_ids(component)
76
- Decidim::Budgets::Order.where(component: component).joins(:component)
77
- .finished
78
- .pluck(:decidim_user_id).flatten.compact.uniq
96
+ Decidim::Budgets::Order.finished
97
+ .joins(budget: [:component])
98
+ .where(budget: {
99
+ decidim_components: { id: component.id }
100
+ })
101
+ .pluck(:decidim_user_id)
102
+ .flatten.compact.uniq
79
103
  end
80
104
 
81
105
  private
82
106
 
83
107
  def user_belongs_to_organization
84
- organization = component&.organization
108
+ organization = budget.try(:component).try(:organization)
85
109
 
86
110
  return if !user || !organization
87
111
 
88
112
  errors.add(:user, :invalid) unless user.organization == organization
89
113
  end
114
+
115
+ def reach_minimum_projects
116
+ return unless minimum_projects_rule?
117
+
118
+ errors.add(:projects, :invalid) if minimum_projects > projects.count
119
+ end
90
120
  end
91
121
  end
92
122
  end
@@ -6,8 +6,7 @@ module Decidim
6
6
  # title, description and any other useful information to render a custom project.
7
7
  class Project < Budgets::ApplicationRecord
8
8
  include Decidim::Resourceable
9
- include Decidim::HasComponent
10
- include Decidim::ScopableComponent
9
+ include Decidim::ScopableResource
11
10
  include Decidim::HasCategory
12
11
  include Decidim::HasAttachments
13
12
  include Decidim::HasAttachmentCollections
@@ -18,11 +17,17 @@ module Decidim
18
17
  include Decidim::Loggable
19
18
  include Decidim::Randomable
20
19
  include Decidim::Searchable
20
+ include Decidim::TranslatableResource
21
21
 
22
- component_manifest_name "budgets"
22
+ translatable_fields :title, :description
23
+
24
+ belongs_to :budget, foreign_key: "decidim_budgets_budget_id", class_name: "Decidim::Budgets::Budget", inverse_of: :projects
25
+ has_one :component, through: :budget, foreign_key: "decidim_component_id", class_name: "Decidim::Component"
23
26
  has_many :line_items, class_name: "Decidim::Budgets::LineItem", foreign_key: "decidim_project_id", dependent: :destroy
24
27
  has_many :orders, through: :line_items, foreign_key: "decidim_project_id", class_name: "Decidim::Budgets::Order"
25
28
 
29
+ delegate :organization, :participatory_space, to: :component
30
+
26
31
  searchable_fields(
27
32
  scope_id: :decidim_scope_id,
28
33
  participatory_space: { component: :participatory_space },
@@ -31,10 +36,26 @@ module Decidim
31
36
  datetime: :created_at
32
37
  )
33
38
 
39
+ def self.ordered_ids(ids)
40
+ # Make sure each ID in the matching text has a "," character as their
41
+ # delimiter. Otherwise e.g. ID 2 would match ID "26" in the original
42
+ # array. This is why we search for match ",2," instead to get the actual
43
+ # position for ID 2.
44
+ order(Arel.sql("position(concat(',', id::text, ',') in ',#{ids.join(",")},')"))
45
+ end
46
+
34
47
  def self.log_presenter_class_for(_log)
35
48
  Decidim::Budgets::AdminLog::ProjectPresenter
36
49
  end
37
50
 
51
+ def polymorphic_resource_path(url_params)
52
+ ::Decidim::ResourceLocatorPresenter.new([budget, self]).path(url_params)
53
+ end
54
+
55
+ def polymorphic_resource_url(url_params)
56
+ ::Decidim::ResourceLocatorPresenter.new([budget, self]).url(url_params)
57
+ end
58
+
38
59
  # Public: Overrides the `commentable?` Commentable concern method.
39
60
  def commentable?
40
61
  component.settings.comments_enabled?
@@ -67,7 +88,19 @@ module Decidim
67
88
 
68
89
  # Public: Whether the object can have new comments or not.
69
90
  def user_allowed_to_comment?(user)
70
- can_participate_in_space?(user)
91
+ component.can_participate_in_space?(user)
92
+ end
93
+
94
+ # Public: Checks if the project has been selected or not.
95
+ #
96
+ # Returns Boolean.
97
+ def selected?
98
+ selected_at.present?
99
+ end
100
+
101
+ # Public: Returns the attachment context for this record.
102
+ def attachment_context
103
+ :admin
71
104
  end
72
105
  end
73
106
  end
@@ -5,18 +5,27 @@ module Decidim
5
5
  module Admin
6
6
  class Permissions < Decidim::DefaultPermissions
7
7
  def permissions
8
- # The public part needs to be implemented yet
9
8
  return permission_action if permission_action.scope != :admin
10
9
 
11
- return permission_action unless [:project, :projects].include?(permission_action.subject)
12
-
13
- case permission_action.action
14
- when :create
15
- permission_action.allow!
16
- when :import_proposals
17
- permission_action.allow!
18
- when :update, :destroy
19
- permission_action.allow! if project.present?
10
+ case permission_action.subject
11
+ when :budget
12
+ case permission_action.action
13
+ when :create, :read
14
+ allow!
15
+ when :update
16
+ toggle_allow(budget)
17
+ when :delete, :publish, :unpublish
18
+ toggle_allow(budget && budget.projects.empty?)
19
+ end
20
+ when :project, :projects
21
+ case permission_action.action
22
+ when :create
23
+ permission_action.allow!
24
+ when :import_proposals
25
+ permission_action.allow!
26
+ when :update, :destroy
27
+ permission_action.allow! if project.present?
28
+ end
20
29
  end
21
30
 
22
31
  permission_action
@@ -24,6 +33,10 @@ module Decidim
24
33
 
25
34
  private
26
35
 
36
+ def budget
37
+ @budget ||= context.fetch(:budget, nil)
38
+ end
39
+
27
40
  def project
28
41
  @project ||= context.fetch(:project, nil)
29
42
  end
@@ -10,15 +10,19 @@ module Decidim
10
10
  return Decidim::Budgets::Admin::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :admin
11
11
  return permission_action if permission_action.scope != :public
12
12
 
13
- return permission_action if permission_action.subject != :project
13
+ return permission_action unless [:project, :order].include? permission_action.subject
14
14
 
15
- case permission_action.action
16
- when :vote
17
- can_vote_project?(project || order&.projects&.first)
18
- when :report
19
- permission_action.allow!
15
+ if permission_action.subject == :project
16
+ case permission_action.action
17
+ when :vote
18
+ can_vote?(false) if can_vote_project?(project || order&.projects&.first)
19
+ when :report
20
+ permission_action.allow!
21
+ end
20
22
  end
21
23
 
24
+ can_vote?(true) if permission_action.action == :create && permission_action.subject == :order
25
+
22
26
  permission_action
23
27
  end
24
28
 
@@ -32,6 +36,24 @@ module Decidim
32
36
  @order ||= context.fetch(:order, nil)
33
37
  end
34
38
 
39
+ def budget
40
+ @budget ||= context.fetch(:budget, nil)
41
+ end
42
+
43
+ def workflow
44
+ @workflow ||= context.fetch(:workflow, nil)
45
+ end
46
+
47
+ def can_vote?(active_allow)
48
+ is_allowed = workflow.vote_allowed?(budget)
49
+
50
+ if !is_allowed
51
+ disallow!
52
+ elsif active_allow
53
+ allow!
54
+ end
55
+ end
56
+
35
57
  def can_vote_project?(a_project)
36
58
  is_allowed = a_project && authorized?(:vote, resource: project)
37
59
 
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ module AdminLog
6
+ # This class holds the logic to present a `Decidim::Budgets::Budget`
7
+ # for the `AdminLog` log.
8
+ #
9
+ # Usage should be automatic and you shouldn't need to call this class
10
+ # directly, but here's an example:
11
+ #
12
+ # action_log = Decidim::ActionLog.last
13
+ # view_helpers # => this comes from the views
14
+ # BudgetPresenter.new(action_log, view_helpers).present
15
+ class BudgetPresenter < Decidim::Log::BasePresenter
16
+ private
17
+
18
+ def action_string
19
+ case action
20
+ when "create", "delete", "update"
21
+ "decidim.budgets.admin_log.budget.#{action}"
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def diff_fields_mapping
28
+ {
29
+ title: :i18n,
30
+ weight: :integer,
31
+ description: :i18n,
32
+ total_budget: :currency
33
+ }
34
+ end
35
+
36
+ def i18n_labels_scope
37
+ "activemodel.attributes.budget"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -27,11 +27,19 @@ module Decidim
27
27
  # Finds the Projects scoped to an array of components and filtered
28
28
  # by a range of dates.
29
29
  def query
30
- projects = Decidim::Budgets::Project.where(component: @components)
30
+ projects = Decidim::Budgets::Project.where(budget: budgets)
31
31
  projects = projects.where("created_at >= ?", @start_at) if @start_at.present?
32
32
  projects = projects.where("created_at <= ?", @end_at) if @end_at.present?
33
33
  projects
34
34
  end
35
+
36
+ private
37
+
38
+ attr_reader :components
39
+
40
+ def budgets
41
+ @budgets ||= Decidim::Budgets::Budget.where(component: components).order(weight: :asc)
42
+ end
35
43
  end
36
44
  end
37
45
  end