pg_rails 7.6.20 → 7.6.21.pre.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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/pg_associable/app/javascript/modal_controller.js +8 -4
  3. data/pg_associable/spec/pg_associable/helpers_spec.rb +8 -13
  4. data/pg_engine/app/components/inline_edit/inline_edit_component.html.slim +1 -1
  5. data/pg_engine/app/components/inline_edit/inline_show_component.html.slim +2 -2
  6. data/pg_engine/app/components/notifications_bell_component.rb +1 -1
  7. data/pg_engine/app/controllers/admin/accounts_controller.rb +2 -2
  8. data/pg_engine/app/controllers/admin/user_accounts_controller.rb +3 -3
  9. data/pg_engine/app/controllers/admin/users_controller.rb +4 -1
  10. data/pg_engine/app/controllers/concerns/pg_engine/resource.rb +12 -7
  11. data/pg_engine/app/controllers/concerns/pg_engine/tenant_helper.rb +33 -0
  12. data/pg_engine/app/controllers/pg_engine/base_admin_controller.rb +0 -1
  13. data/pg_engine/app/controllers/pg_engine/base_controller.rb +18 -21
  14. data/pg_engine/app/controllers/pg_engine/base_public_controller.rb +1 -1
  15. data/pg_engine/app/controllers/pg_engine/base_users_controller.rb +5 -3
  16. data/pg_engine/app/controllers/pg_engine/devise_controller.rb +4 -1
  17. data/pg_engine/app/controllers/pg_engine/tenant_controller.rb +22 -0
  18. data/pg_engine/app/controllers/{users → tenant}/dashboard_controller.rb +2 -2
  19. data/pg_engine/app/controllers/tenant/inline_edit_controller.rb +22 -0
  20. data/pg_engine/app/controllers/tenant/user_accounts_controller.rb +56 -0
  21. data/pg_engine/app/controllers/users/accounts_controller.rb +46 -15
  22. data/pg_engine/app/controllers/users/invitations_controller.rb +78 -0
  23. data/pg_engine/app/controllers/users/notifications_controller.rb +1 -0
  24. data/pg_engine/app/controllers/users/registrations_controller.rb +3 -2
  25. data/pg_engine/app/decorators/account_decorator.rb +18 -0
  26. data/pg_engine/app/decorators/pg_engine/base_record_decorator.rb +9 -1
  27. data/pg_engine/app/decorators/user_account_decorator.rb +76 -11
  28. data/pg_engine/app/helpers/pg_engine/accounts_helper.rb +9 -0
  29. data/pg_engine/app/helpers/pg_engine/frame_helper.rb +12 -0
  30. data/pg_engine/app/lib/pg_engine/default_url_options.rb +21 -0
  31. data/pg_engine/app/lib/pg_engine/filtros_builder.rb +2 -0
  32. data/pg_engine/app/models/account.rb +28 -1
  33. data/pg_engine/app/models/concerns/pg_engine/child_record.rb +55 -0
  34. data/pg_engine/app/models/current.rb +29 -3
  35. data/pg_engine/app/models/pg_engine/base_record.rb +5 -0
  36. data/pg_engine/app/models/user.rb +56 -14
  37. data/pg_engine/app/models/user_account.rb +74 -9
  38. data/pg_engine/app/policies/account_policy.rb +35 -16
  39. data/pg_engine/app/policies/email_log_policy.rb +0 -4
  40. data/pg_engine/app/policies/email_policy.rb +0 -4
  41. data/pg_engine/app/policies/pg_engine/base_policy.rb +31 -10
  42. data/pg_engine/app/policies/user_account_policy.rb +59 -25
  43. data/pg_engine/app/policies/user_policy.rb +14 -1
  44. data/pg_engine/app/views/admin/user_accounts/_form.html.slim +1 -1
  45. data/pg_engine/app/views/admin/user_accounts/show.html.slim +15 -0
  46. data/pg_engine/app/views/admin/users/show.html.slim +0 -3
  47. data/pg_engine/app/views/pg_engine/base/index.html.slim +2 -2
  48. data/pg_engine/app/views/tenant/dashboard/dashboard.html.slim +2 -0
  49. data/pg_engine/app/views/tenant/user_accounts/_fields.html.slim +13 -0
  50. data/pg_engine/app/views/tenant/user_accounts/_form.html.slim +9 -0
  51. data/pg_engine/app/views/tenant/user_accounts/show.html.slim +20 -0
  52. data/pg_engine/app/views/users/accounts/_form.html.slim +7 -0
  53. data/pg_engine/app/views/users/accounts/show.html.slim +23 -15
  54. data/pg_engine/config/initializers/acts_as_tenant.rb +7 -2
  55. data/pg_engine/config/initializers/devise.rb +10 -0
  56. data/pg_engine/config/initializers/ransack.rb +2 -0
  57. data/pg_engine/config/locales/es.yml +60 -0
  58. data/pg_engine/config/routes.rb +21 -11
  59. data/pg_engine/config/simple_form/simple_form_bootstrap.rb +2 -2
  60. data/pg_engine/db/migrate/20241023203849_devise_invitable.rb +14 -0
  61. data/pg_engine/db/migrate/20241027225618_add_membership_status_to_user_accounts.rb +6 -0
  62. data/pg_engine/db/seeds.rb +6 -6
  63. data/pg_engine/lib/pg_engine/configuracion.rb +36 -1
  64. data/pg_engine/lib/pg_engine/navigator.rb +2 -25
  65. data/pg_engine/lib/pg_engine.rb +1 -0
  66. data/pg_engine/spec/controllers/admin/accounts_controller_spec.rb +3 -2
  67. data/pg_engine/spec/controllers/admin/user_accounts_controller_spec.rb +11 -9
  68. data/pg_engine/spec/factories/accounts.rb +8 -0
  69. data/pg_engine/spec/factories/user_accounts.rb +1 -1
  70. data/pg_engine/spec/factories/users.rb +6 -0
  71. data/pg_engine/spec/models/concerns/pg_engine/child_record_spec.rb +27 -0
  72. data/pg_engine/spec/models/user_account_spec.rb +5 -1
  73. data/pg_engine/spec/models/user_spec.rb +3 -25
  74. data/pg_engine/spec/policies/account_policy_spec.rb +19 -0
  75. data/pg_engine/spec/requests/devise/invitations_spec.rb +196 -0
  76. data/pg_engine/spec/requests/resource_spec.rb +14 -15
  77. data/pg_engine/spec/requests/users/accounts_spec.rb +117 -8
  78. data/pg_engine/spec/requests/users/base_controller_spec.rb +31 -0
  79. data/pg_engine/spec/requests/users/dashboard_spec.rb +4 -9
  80. data/pg_engine/spec/requests/users/date_jumper_spec.rb +2 -1
  81. data/pg_engine/spec/requests/users/inline_edit_spec.rb +6 -5
  82. data/pg_engine/spec/requests/users/registrations_spec.rb +2 -2
  83. data/pg_engine/spec/requests/users/user_accounts_spec.rb +54 -0
  84. data/pg_engine/spec/system/login_spec.rb +2 -2
  85. data/pg_engine/spec/system/noticed_spec.rb +0 -2
  86. data/pg_engine/spec/system/signup_spec.rb +4 -3
  87. data/pg_engine/spec/system/tenants_spec.rb +10 -9
  88. data/pg_layout/app/javascript/application.js +1 -1
  89. data/pg_layout/app/javascript/config/turbo_rails/index.js +1 -1
  90. data/pg_layout/app/lib/navbar.rb +15 -1
  91. data/pg_layout/app/views/devise/invitations/edit.html.erb +34 -0
  92. data/pg_layout/app/views/devise/invitations/new.html.erb +15 -0
  93. data/pg_layout/app/views/devise/mailer/invitation_instructions.html.erb +11 -0
  94. data/pg_layout/app/views/devise/mailer/invitation_instructions.text.erb +11 -0
  95. data/pg_layout/app/views/devise/registrations/edit.html.erb +1 -0
  96. data/pg_layout/app/views/layouts/pg_layout/base.html.slim +7 -5
  97. data/pg_layout/app/views/pg_layout/_navbar.html.erb +7 -6
  98. data/pg_layout/app/views/pg_layout/_sidebar.html.erb +2 -20
  99. data/pg_layout/app/views/pg_layout/_signed_in_links.html.slim +52 -0
  100. data/pg_layout/lib/pg_layout.rb +0 -5
  101. data/pg_rails/lib/pg_rails/pundit_matchers.rb +21 -0
  102. data/pg_rails/lib/pg_rails/tpath_support.rb +73 -0
  103. data/pg_rails/lib/pg_rails.rb +6 -0
  104. data/pg_rails/lib/version.rb +1 -1
  105. data/pg_rails/scss/pg_rails.scss +1 -1
  106. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/controller_spec.rb +1 -1
  107. data/pg_scaffold/lib/generators/pg_slim/templates/index.html.slim +1 -1
  108. metadata +43 -9
  109. data/pg_engine/app/controllers/concerns/pg_engine/require_tenant_set.rb +0 -15
  110. data/pg_engine/app/controllers/users/account_switcher_controller.rb +0 -30
  111. data/pg_engine/app/controllers/users/inline_edit_controller.rb +0 -21
  112. data/pg_engine/app/views/users/account_switcher/list.html.slim +0 -24
  113. data/pg_engine/app/views/users/dashboard/dashboard.html.slim +0 -1
  114. data/pg_engine/spec/requests/users/switcher_spec.rb +0 -84
@@ -1,6 +1,7 @@
1
1
  module Users
2
2
  class RegistrationsController < Devise::RegistrationsController
3
3
  before_action do
4
+ @sidebar = false
4
5
  authorize resource, nil, policy_class: UserRegistrationPolicy
5
6
  end
6
7
 
@@ -11,6 +12,7 @@ module Users
11
12
  resource.save
12
13
  yield resource if block_given?
13
14
  if resource.persisted?
15
+ # FIXME: no va más
14
16
  create_account_for(resource) if ActsAsTenant.current_tenant.blank?
15
17
 
16
18
  expire_data_after_sign_in!
@@ -35,8 +37,7 @@ module Users
35
37
  private
36
38
 
37
39
  def create_account_for(user)
38
- account = Account.create!(nombre: user.email)
39
- user.user_accounts.create!(account:)
40
+ Account.create!(nombre: user.email, creado_por: user)
40
41
  end
41
42
  end
42
43
  end
@@ -13,4 +13,22 @@ class AccountDecorator < PgEngine::BaseRecordDecorator
13
13
  # object.created_at.strftime("%a %m/%d/%y")
14
14
  # end
15
15
  # end
16
+ def extra_actions(*)
17
+ return if Current.namespace == :admin
18
+
19
+ ua = Current.user.user_account_for(object).decorate
20
+ [ua.ingresar_link,
21
+ ua.accept_invitation_link,
22
+ ua.reject_invitation_link].compact.join.html_safe
23
+ end
24
+
25
+ # def show_link(text: '', klass: 'btn-light')
26
+ # return unless Pundit.policy!(Current.user, object).show?
27
+
28
+ # helpers.content_tag :span, 'data-controller': :tooltip, title: 'Más opciones' do
29
+ # helpers.link_to object_url, class: "btn btn-sm #{klass}" do
30
+ # helpers.content_tag(:span, nil, class: clase_icono('list')) + text
31
+ # end
32
+ # end
33
+ # end
16
34
  end
@@ -65,8 +65,16 @@ module PgEngine
65
65
  return unless Pundit.policy!(Current.user, object).destroy?
66
66
 
67
67
  helpers.content_tag :span, rel: :tooltip, title: 'Eliminar definitivamente' do
68
+ # :nocov:
69
+ confirm_key = if Rails.env.development?
70
+ 'noconfirm'
71
+ else
72
+ 'turbo-confirm'
73
+ end
74
+ # :nocov:
75
+
68
76
  helpers.link_to object_url + (land_on.present? ? "?land_on=#{land_on}" : ''),
69
- data: { 'turbo-confirm': confirm_text, 'turbo-method': :delete },
77
+ data: { "#{confirm_key}": confirm_text, 'turbo-method': :delete },
70
78
  class: "btn btn-sm #{klass} text-danger" do
71
79
  helpers.content_tag :span, nil, class: clase_icono('trash-fill')
72
80
  end
@@ -5,15 +5,80 @@
5
5
  class UserAccountDecorator < PgEngine::BaseRecordDecorator
6
6
  delegate_all
7
7
 
8
- def profiles
9
- object.profiles.join(', ')
10
- end
11
- # Define presentation-specific methods here. Helpers are accessed through
12
- # `helpers` (aka `h`). You can override attributes, for example:
13
- #
14
- # def created_at
15
- # helpers.content_tag :span, class: 'time' do
16
- # object.created_at.strftime("%a %m/%d/%y")
17
- # end
18
- # end
8
+ def ingresar_link
9
+ return unless Pundit.policy!(Current.user, object).ingresar?
10
+
11
+ h.link_to h.tenant_root_path(tid: object.to_param),
12
+ 'data-turbo-frame': :_top,
13
+ class: 'btn btn-sm btn-primary' do
14
+ '<i class="bi bi-box-arrow-in-right"></i> Ingresar'.html_safe
15
+ end
16
+ end
17
+
18
+ def accept_invitation_link
19
+ return unless Pundit.policy!(Current.user, object).accept_invitation_link?
20
+
21
+ h.link_to [:update_invitation, :users, account, { accept: 1 }].flatten,
22
+ 'data-turbo-method': :put,
23
+ class: 'btn btn-sm btn-success' do
24
+ 'Aceptar invitación'
25
+ end
26
+ end
27
+
28
+ def reject_invitation_link
29
+ return unless Pundit.policy!(Current.user, object).accept_invitation_link?
30
+
31
+ h.link_to [:update_invitation, :users, account, { reject: 1 }].flatten,
32
+ 'data-turbo-method': :put,
33
+ class: 'btn btn-sm btn-danger' do
34
+ 'Rechazar'
35
+ end
36
+ end
37
+
38
+ def sign_off_link
39
+ return unless Pundit.policy!(Current.user, object).sign_off?
40
+
41
+ h.link_to [:update_invitation, :users, account, { sign_off: 1 }].flatten,
42
+ 'data-turbo-method': :put,
43
+ class: 'btn btn-sm btn-outline-danger' do
44
+ 'Dejar la cuenta'
45
+ end
46
+ end
47
+
48
+ def estado_f
49
+ membership_status_f + ' - ' + invitation_status_f
50
+ end
51
+
52
+ def membership_status_f
53
+ klass = {
54
+ 'ms_active' => 'text-success',
55
+ 'ms_disabled' => 'text-danger'
56
+ }[object.membership_status]
57
+
58
+ content_tag :span, object.membership_status_text, class: "#{klass} fw-bold"
59
+ end
60
+
61
+ def invitation_status_f
62
+ klass = {
63
+ 'ist_accepted' => 'text-success',
64
+ 'ist_invited' => 'text-warning-emphasis',
65
+ 'ist_rejected' => 'text-danger',
66
+ 'ist_signed_off' => 'text-danger'
67
+ }[object.invitation_status]
68
+
69
+ content_tag :span, object.invitation_status_text, class: "#{klass} fw-bold"
70
+ end
71
+
72
+ def profiles_f
73
+ return if object.profiles.account__owner?
74
+
75
+ # object.profiles.texts.join(', ')
76
+ PgEngine.config.profile_groups_options.map do |profile_group|
77
+ "<b>#{I18n.t(profile_group[:name], scope: 'profile_group')}: </b>" + h.show_profiles_for(object, profile_group)
78
+ end.join('. ').html_safe
79
+ end
80
+
81
+ def user_email_f
82
+ user.email
83
+ end
19
84
  end
@@ -0,0 +1,9 @@
1
+ module PgEngine
2
+ module AccountsHelper
3
+ def show_profiles_for(user_account, profile_group)
4
+ aux = profile_group[:options].select { |opt| user_account.profiles.include?(opt.first) }
5
+ aux = aux.map(&:last).map { |pr| t(pr, scope: 'profile_member') }.join(', ').html_safe
6
+ aux.presence || 'No'
7
+ end
8
+ end
9
+ end
@@ -32,6 +32,18 @@ module PgEngine
32
32
  end
33
33
 
34
34
  def embed_index(object, key)
35
+ reflection = object.class.reflect_on_all_associations.find do |a|
36
+ a.name == key.to_sym
37
+ end
38
+
39
+ if reflection.blank?
40
+ # :nocov:
41
+ raise PgEngine::Error, "#{key} not an association for #{object.class}"
42
+ # :nocov:
43
+ end
44
+
45
+ return unless policy(reflection.klass).index?
46
+
35
47
  content_tag(:div, 'data-controller': 'embedded-frame') do
36
48
  turbo_frame_tag "embedded--#{key}",
37
49
  refresh: :morph, src: url_for([pg_namespace, object, key]) do
@@ -0,0 +1,21 @@
1
+ module PgEngine
2
+ module DefaultUrlOptions
3
+ # Aunque parece intuitivo que se podría definir solamente url_options,
4
+ # es importante que default_url_options también esté definido
5
+ def url_options
6
+ if Current.active_user_account
7
+ super.merge(tid: Current.active_user_account.to_param)
8
+ else
9
+ super.merge(tid: nil)
10
+ end
11
+ end
12
+
13
+ def default_url_options
14
+ if Current.active_user_account
15
+ { tid: Current.active_user_account.to_param }
16
+ else
17
+ { tid: nil }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -19,11 +19,13 @@ module PgEngine
19
19
  incluye
20
20
  es_igual_a
21
21
  in
22
+ arr_cont
22
23
  cont_all
23
24
  cont_any
24
25
  not_cont
25
26
  cont
26
27
  eq
28
+ matches
27
29
  ].freeze
28
30
 
29
31
  def initialize(controller, clase_modelo, campos)
@@ -24,7 +24,7 @@ class Account < ApplicationRecord
24
24
  include Discard::Model
25
25
  include Hashid::Rails
26
26
 
27
- has_many :user_accounts
27
+ has_many :user_accounts, dependent: :destroy
28
28
  has_many :users, through: :user_accounts
29
29
 
30
30
  belongs_to :creado_por, optional: true, class_name: 'User'
@@ -36,15 +36,42 @@ class Account < ApplicationRecord
36
36
 
37
37
  has_many :audits, dependent: :nullify, class_name: 'Audited::Audit'
38
38
 
39
+ has_one_attached :logo do |attachable|
40
+ attachable.variant :thumb, resize_and_pad: [80, 80]
41
+ end
42
+
39
43
  ransacker :search do |parent|
40
44
  parent.table[:nombre]
41
45
  end
42
46
 
47
+ after_create do
48
+ if creado_por.present?
49
+ ActsAsTenant.without_tenant do
50
+ user_accounts.create!(account: self, user: creado_por, profiles: [:account__owner])
51
+ end
52
+ end
53
+ end
54
+
43
55
  before_validation do
44
56
  self.plan = 0 if plan.blank?
45
57
  end
46
58
 
59
+ def self.gender
60
+ 'm'
61
+ end
62
+
47
63
  def to_s
48
64
  nombre
49
65
  end
66
+
67
+ # There can be only one
68
+ def owner
69
+ @owner ||= ActsAsTenant.without_tenant do
70
+ user_accounts.ua_active.owners.first&.user
71
+ end
72
+
73
+ raise PgEngine::Error, 'orphan account' if @owner.nil?
74
+
75
+ @owner
76
+ end
50
77
  end
@@ -0,0 +1,55 @@
1
+ module PgEngine
2
+ module ChildRecord
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ scope :kept, -> { undiscarded.joins(klass.parent_accessor).merge(klass.parent_klass.kept) }
7
+ scope :unkept, -> { discarded.joins(klass.parent_accessor).or(klass.parent_klass.discarded) }
8
+ end
9
+
10
+ class_methods do
11
+ attr_accessor :parent_accessor
12
+
13
+ def parent_klass
14
+ if parent_accessor.blank?
15
+ # :nocov:
16
+ raise PgEngine::Error, 'parent_accessor must be present'
17
+ # :nocov:
18
+ end
19
+
20
+ reflection = reflect_on_all_associations.select { |r| r.name == parent_accessor.to_sym }.first
21
+ if reflection.blank?
22
+ # :nocov:
23
+ raise PgEngine::Error, "#{parent_accessor} not an association on #{self}"
24
+ # :nocov:
25
+ end
26
+
27
+ reflection.klass
28
+ end
29
+ end
30
+
31
+ def parent?
32
+ if self.class.parent_accessor.blank?
33
+ # :nocov:
34
+ raise PgEngine::Error, 'parent_accessor must be present'
35
+ # :nocov:
36
+ end
37
+
38
+ true
39
+ end
40
+
41
+ def parent
42
+ if self.class.parent_accessor.blank?
43
+ # :nocov:
44
+ raise PgEngine::Error, 'parent_accessor must be present'
45
+ # :nocov:
46
+ end
47
+
48
+ send(self.class.parent_accessor)
49
+ end
50
+
51
+ def kept?
52
+ undiscarded? && (parent.blank? || parent.kept?)
53
+ end
54
+ end
55
+ end
@@ -1,14 +1,40 @@
1
1
  class Current < ActiveSupport::CurrentAttributes
2
- attribute :account, :user, :namespace, :controller
2
+ attribute :user, :namespace, :controller, :active_user_account
3
3
  # attribute :request_id, :user_agent, :ip_address
4
4
 
5
5
  # resets { Time.zone = nil }
6
+ def active_user_account
7
+ # Para los jobs
8
+ if attributes[:active_user_account].nil? && user.present? && account.present?
9
+ attributes[:active_user_account] = user.active_user_account_for(account)
10
+ end
11
+
12
+ super
13
+ end
14
+
15
+ def tid
16
+ active_user_account.to_param
17
+ end
18
+
19
+ def active_user_profiles
20
+ if active_user_account.present?
21
+ active_user_account.profiles
22
+ else
23
+ []
24
+ end
25
+ end
26
+
27
+ def user_account_owner?
28
+ active_user_profiles.include?('account__owner')
29
+ end
30
+
31
+ def account
32
+ ActsAsTenant.current_tenant
33
+ end
6
34
 
7
35
  # def user=(user)
8
36
  # super
9
37
  #
10
38
  # Time.zone = user.time_zone
11
39
  # end
12
-
13
- deprecate :account, deprecator: PgEngine.deprecator
14
40
  end
@@ -28,6 +28,11 @@ module PgEngine
28
28
  end
29
29
  end
30
30
 
31
+ # overriden by PgEngine::ChildRecord
32
+ def parent?
33
+ false
34
+ end
35
+
31
36
  def to_s
32
37
  %i[nombre name].each do |campo|
33
38
  # Using `_in_database` for consistent breadcrumbs when editing the name
@@ -29,24 +29,49 @@
29
29
  #
30
30
 
31
31
  class User < ApplicationRecord
32
+ default_scope lambda {
33
+ if Current.namespace == :tenant
34
+ if ActsAsTenant.unscoped?
35
+ all
36
+ else
37
+ ids = Current.account.user_accounts.ua_active.pluck(:user_id)
38
+ # ids = ids.push(Current.user.id) ?
39
+ where(id: ids)
40
+ end
41
+ else
42
+ all
43
+ end
44
+ }
45
+
32
46
  devise :database_authenticatable, :registerable,
33
47
  :recoverable, :rememberable,
34
- :lockable, :timeoutable, :trackable, :confirmable
48
+ :lockable, :timeoutable, :trackable, :confirmable, :invitable
35
49
 
36
50
  audited
37
51
  include Discard::Model
38
52
 
39
53
  has_many :user_accounts, dependent: :destroy
54
+ accepts_nested_attributes_for :user_accounts
40
55
 
41
56
  # Hace falta?
42
57
  has_many :accounts, through: :user_accounts
43
58
 
44
- acts_as_tenant :account, through: :user_accounts
59
+ # Crea automáticamente una user_account on create
60
+ # a menos que ya exista en los nested attributes una user
61
+ # account para la current tenant
62
+ #
63
+ # Es problemático porque interfiere en UserAccount.joins(:user)
64
+ # y hace un doble join
65
+ # acts_as_tenant :account, through: :user_accounts
45
66
 
46
67
  has_many :notifications, as: :recipient, class_name: 'Noticed::Notification'
47
68
 
48
69
  validates :nombre, :apellido, presence: true
49
70
 
71
+ has_one_attached :avatar do |attachable|
72
+ attachable.variant :thumb, resize_to_fill: [80, 80]
73
+ end
74
+
50
75
  validates_presence_of :email
51
76
  validates_uniqueness_of :email, message: 'ya pertenece a un usuario'
52
77
  validates_format_of :email, with: /\A[^@\s]+@[^@\s]+\z/
@@ -59,6 +84,11 @@ class User < ApplicationRecord
59
84
  message: 'Para crear una cuenta es necesario que aceptes los términos y condiciones'
60
85
  }
61
86
 
87
+ # When the user is invited via Devise Invitable
88
+ before_invitation_created do
89
+ user_accounts.first.invitation_status = :ist_invited
90
+ end
91
+
62
92
  attr_accessor :orphan
63
93
 
64
94
  def active_for_authentication?
@@ -93,7 +123,7 @@ class User < ApplicationRecord
93
123
  end
94
124
 
95
125
  def to_s
96
- nombre_completo
126
+ nombre_completo.strip.presence || email
97
127
  end
98
128
 
99
129
  def nombre_completo
@@ -102,22 +132,34 @@ class User < ApplicationRecord
102
132
 
103
133
  class Error < PgEngine::Error; end
104
134
 
105
- def user_accounts_without_tenant
106
- ActsAsTenant.without_tenant do
107
- user_accounts.to_a
108
- end
109
- end
110
-
111
- def default_account
112
- raise Error, 'El usuario debe tener cuenta' if accounts.empty?
135
+ # def default_account
136
+ # raise Error, 'El usuario debe tener cuenta' if accounts.empty?
113
137
 
114
- user_accounts.first.account
115
- # throw :warden, scope: :user, message: :user_not_belongs_to_account
116
- end
138
+ # user_accounts.first.account
139
+ # # throw :warden, scope: :user, message: :user_not_belongs_to_account
140
+ # end
117
141
 
118
142
  deprecate :current_account, deprecator: PgEngine.deprecator
119
143
 
120
144
  def current_account
121
145
  ActsAsTenant.current_tenant
122
146
  end
147
+
148
+ def active_user_account_for(account)
149
+ user_account_for(account, active: true)
150
+ end
151
+
152
+ def user_account_for(account, active: false)
153
+ if account.nil?
154
+ # :nocov:
155
+ raise PgEngine::Error, 'account must be present'
156
+ # :nocov:
157
+ end
158
+
159
+ scope_name = active ? :ua_active : :kept
160
+
161
+ ActsAsTenant.without_tenant do
162
+ user_accounts.send(scope_name).where(account:).first
163
+ end
164
+ end
123
165
  end
@@ -21,24 +21,89 @@
21
21
  # fk_rails_... (user_id => users.id)
22
22
  #
23
23
 
24
- # FIXME: add column active?
25
24
  class UserAccount < ApplicationRecord
26
25
  audited
27
26
  include Hashid::Rails
28
27
 
28
+ # self.inline_editable_fields = %i[profiles]
29
+ self.default_modal = true
30
+
29
31
  belongs_to :user, inverse_of: :user_accounts
32
+
30
33
  acts_as_tenant :account
31
34
 
35
+ def self.gender
36
+ 'f'
37
+ end
38
+
32
39
  belongs_to :creado_por, optional: true, class_name: 'User'
33
40
  belongs_to :actualizado_por, optional: true, class_name: 'User'
34
41
 
35
- # scope :kept, -> { undiscarded.joins(:account).merge(Account.kept) }
36
- # FIXME: merge with User.kept
37
- scope :kept, -> { joins(:account).merge(Account.kept) }
42
+ validates :user_id, uniqueness: { scope: :account_id }
43
+
44
+ after_destroy :cleanup_invitation
45
+ def cleanup_invitation
46
+ usr = User.unscoped.find(user_id)
47
+ return unless usr.invited_to_sign_up? && !usr.confirmed?
48
+ return if usr.destroy
49
+ # :nocov:
50
+
51
+ pg_err 'User couldnt be deleted on invitation cleanup'
52
+ # :nocov:
53
+ end
54
+ # El problema está en el joins(:user), ya que la default scope de user está scopeada dentro
55
+ # del current_tenant entonces vuelve sobre la tabla user_accounts y bardea
56
+ #
57
+ # Tengo que escribir el user joins a mano porque de lo contrario sumaría la default_scope de
58
+ # User, que a su vez joinea con user_accounts
59
+ USER_JOINS = 'INNER JOIN users ON users.id = user_accounts.user_id'
60
+ scope :kept, -> { joins(USER_JOINS, :account).merge(Account.kept).merge(User.unscoped.kept) }
61
+
62
+ scope :ua_active, lambda {
63
+ kept.where(membership_status: :ms_active, invitation_status: :ist_accepted)
64
+ }
65
+
66
+ OWNERS_PREDICATE = lambda do
67
+ UserAccount.arel_table[:profiles].contains([UserAccount.profiles.account__owner.value])
68
+ end
69
+
70
+ scope :owners, lambda {
71
+ where(OWNERS_PREDICATE.call)
72
+ }
73
+
74
+ scope :regulars, lambda {
75
+ where.not(OWNERS_PREDICATE.call)
76
+ }
77
+
78
+ # Se usa en schema.rb, default: 1
79
+ enumerize :membership_status, in: {
80
+ ms_active: 1,
81
+ ms_disabled: 2
82
+ }
83
+
84
+ # Se usa en schema.rb, default: 1
85
+ enumerize :invitation_status, in: {
86
+ ist_accepted: 1,
87
+ ist_invited: 2,
88
+ ist_rejected: 3,
89
+ ist_signed_off: 4
90
+ }
91
+
92
+ scope :not_discarded_by_user, -> { where.not(invitation_status: %i[ist_rejected ist_signed_off]) }
93
+
94
+ def discarded_by_user?
95
+ invitation_status.ist_rejected? || invitation_status.ist_signed_off?
96
+ end
97
+
98
+ def ua_active?
99
+ invitation_status.ist_accepted? && membership_status.ms_active?
100
+ end
101
+
102
+ def ua_invite_pending?
103
+ invitation_status.ist_invited? && membership_status.ms_active?
104
+ end
105
+
106
+ enumerize :profiles, in: PgEngine.configuracion.user_profiles, multiple: true
38
107
 
39
- enumerize :profiles, in: {
40
- administracion: 1,
41
- operacion: 2,
42
- lectura: 3
43
- }, multiple: true
108
+ delegate :to_s, to: :user
44
109
  end
@@ -4,37 +4,56 @@
4
4
 
5
5
  class AccountPolicy < ApplicationPolicy
6
6
  class Scope < ApplicationPolicy::Scope
7
- # def resolve
8
- # if policy.acceso_total?
9
- # scope.all
10
- # else
11
- # scope.none
12
- # end
13
- # end
7
+ def resolve
8
+ if Current.namespace == :admin
9
+ scope.all
10
+ else
11
+ ary = ActsAsTenant.without_tenant do
12
+ Current.user.user_accounts.kept.not_discarded_by_user.pluck(:account_id)
13
+ end
14
+ scope.where(id: ary)
15
+ end
16
+ end
17
+ end
18
+
19
+ def update_invitation?
20
+ user_belongs_to_account?
14
21
  end
15
22
 
16
23
  def puede_editar?
17
- user.developer?
24
+ Current.namespace == :admin || record.owner == Current.user
18
25
  end
19
26
 
20
27
  def puede_crear?
21
- user.developer?
28
+ user.present?
22
29
  end
23
30
 
24
31
  def puede_borrar?
25
- user.developer?
32
+ Current.namespace == :admin
26
33
  end
27
34
 
28
- # def acceso_total?
29
- # user.developer?
30
- # end
31
-
32
35
  def new_from_associable?
33
36
  false
34
37
  end
35
38
 
39
+ def show?
40
+ base_access_to_record?
41
+ end
42
+
43
+ def index?
44
+ base_access_to_collection?
45
+ end
46
+
47
+ def base_access_to_collection?
48
+ user.present?
49
+ end
50
+
36
51
  def base_access_to_record?
37
- ActsAsTenant.unscoped? ||
38
- record.user_accounts.pluck(:user_id).include?(user.id)
52
+ ua = user.user_account_for(record)
53
+ Current.namespace == :admin || (ua.present? && !ua.ua_invite_pending?)
54
+ end
55
+
56
+ def user_belongs_to_account?
57
+ user.user_account_for(record).present?
39
58
  end
40
59
  end
@@ -24,8 +24,4 @@ class EmailLogPolicy < ApplicationPolicy
24
24
  # def puede_borrar?
25
25
  # acceso_total? && !record.readonly?
26
26
  # end
27
-
28
- # def acceso_total?
29
- # user.developer?
30
- # end
31
27
  end