Skip to content

Acceptance between administrators #790

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: time-between-banks-base
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions app/controllers/organization_alliances_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
class OrganizationAlliancesController < ApplicationController
before_action :authenticate_user!
before_action :member_should_exist_and_be_active
before_action :authorize_admin
before_action :find_alliance, only: [:update, :destroy]

def index
@status = params[:status] || "pending"

@alliances = case @status
when "pending"
current_organization.pending_alliances.includes(:source_organization, :target_organization)
when "accepted"
current_organization.accepted_alliances.includes(:source_organization, :target_organization)
when "rejected"
current_organization.rejected_alliances.includes(:source_organization, :target_organization)
else
[]
end
end

def create
alliance = OrganizationAlliance.new(
source_organization: current_organization,
target_organization_id: alliance_params[:target_organization_id],
status: "pending"
)

if alliance.save
flash[:notice] = t("organization_alliances.created")
else
flash[:error] = alliance.errors.full_messages.to_sentence
end

redirect_back fallback_location: organizations_path
end

def update
if @alliance.update(status: params[:status])
flash[:notice] = t("organization_alliances.updated")
else
flash[:error] = @alliance.errors.full_messages.to_sentence
end

redirect_to organization_alliances_path
end

def destroy
if @alliance.destroy
flash[:notice] = t("organization_alliances.destroyed")
else
flash[:error] = t("organization_alliances.error_destroying")
end

redirect_to organization_alliances_path
end

private

def find_alliance
@alliance = OrganizationAlliance.find(params[:id])
end

def authorize_admin
return if current_user.manages?(current_organization)

flash[:error] = t("organization_alliances.not_authorized")
redirect_to root_path
end

def alliance_params
params.require(:organization_alliance).permit(:target_organization_id)
end
end
21 changes: 8 additions & 13 deletions app/controllers/posts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ class PostsController < ApplicationController
def index
context = model.active.of_active_members

if current_organization.present?
context = context.where(
organization_id: current_organization.id
)
if current_user.present? && current_organization.present?
if params[:show_allied].present?
allied_org_ids = current_organization.allied_organizations.pluck(:id)
org_ids = [current_organization.id] + allied_org_ids
context = context.by_organizations(org_ids)
elsif !params[:org].present?
context = context.by_organization(current_organization.id)
end
end

posts = apply_scopes(context)
Expand Down Expand Up @@ -98,15 +102,6 @@ def post_params
end
end

# TODO: remove this horrible hack ASAP
#
# This hack set the current organization to the post's
# organization, both in session and controller instance variable.
#
# Before changing the current organization it's important to check that
# the current_user is an active member of the organization.
#
# @param organization [Organization]
def update_current_organization!(organization)
return unless current_user && current_user.active?(organization)

Expand Down
25 changes: 25 additions & 0 deletions app/helpers/organizations_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module OrganizationsHelper
def filterable_organizations
Organization.all.order(:name)
end

def allied_organizations
return [] unless current_organization

allied_org_ids = current_organization.accepted_alliances.map do |alliance|
alliance.source_organization_id == current_organization.id ?
alliance.target_organization_id : alliance.source_organization_id
end

organizations = Organization.where(id: allied_org_ids + [current_organization.id])
organizations.order(:name)
end

def alliance_initiator?(alliance)
alliance.source_organization_id == current_organization.id
end

def alliance_recipient(alliance)
alliance_initiator?(alliance) ? alliance.target_organization : alliance.source_organization
end
end
30 changes: 25 additions & 5 deletions app/models/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class Organization < ApplicationRecord
has_many :inquiries
has_many :documents, as: :documentable, dependent: :destroy
has_many :petitions, dependent: :delete_all
has_many :initiated_alliances, class_name: "OrganizationAlliance", foreign_key: "source_organization_id", dependent: :destroy
has_many :received_alliances, class_name: "OrganizationAlliance", foreign_key: "target_organization_id", dependent: :destroy

validates :name, presence: true, uniqueness: true

Expand Down Expand Up @@ -52,15 +54,33 @@ def display_name_with_uid
self
end

# Returns the id to be displayed in the :new transfer page with the given
# destination_accountable
#
# @params destination_accountable [Organization | Object] target of a transfer
# @return [Integer | String]
def display_id
account.accountable_id
end

def alliance_with(organization)
initiated_alliances.find_by(target_organization: organization) ||
received_alliances.find_by(source_organization: organization)
end

def pending_alliances
initiated_alliances.pending.or(received_alliances.pending)
end

def accepted_alliances
initiated_alliances.accepted.or(received_alliances.accepted)
end

def rejected_alliances
initiated_alliances.rejected.or(received_alliances.rejected)
end

def allied_organizations
source_org_ids = initiated_alliances.accepted.pluck(:target_organization_id)
target_org_ids = received_alliances.accepted.pluck(:source_organization_id)
Organization.where(id: source_org_ids + target_org_ids)
end

def ensure_reg_number_seq!
update_column(:reg_number_seq, members.maximum(:member_uid))
end
Expand Down
23 changes: 23 additions & 0 deletions app/models/organization_alliance.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class OrganizationAlliance < ApplicationRecord
belongs_to :source_organization, class_name: "Organization"
belongs_to :target_organization, class_name: "Organization"

enum status: { pending: 0, accepted: 1, rejected: 2 }

validates :source_organization_id, presence: true
validates :target_organization_id, presence: true
validates :target_organization_id, uniqueness: { scope: :source_organization_id }
validate :cannot_ally_with_self

scope :pending, -> { where(status: "pending") }
scope :accepted, -> { where(status: "accepted") }
scope :rejected, -> { where(status: "rejected") }

private

def cannot_ally_with_self
if source_organization_id == target_organization_id
errors.add(:base, "Cannot create an alliance with yourself")
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
<%= t('petitions.applications') %>
<% end %>
</li>
<li>
<%= link_to organization_alliances_path, class: "dropdown-item" do %>
<%= glyph :globe %>
<%= t "application.navbar.organization_alliances" %>
<% end %>
</li>
<li>
<%= link_to offers_path(org: current_organization), class: "dropdown-item" do %>
<%= glyph :link %>
Expand Down
97 changes: 97 additions & 0 deletions app/views/organization_alliances/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<h1><%= t('organization_alliances.title') %></h1>

<div class="row">
<div class="col-12 col-sm-12 col-md-12 col-lg-12">
Comment on lines +3 to +4
Copy link
Collaborator

@franpb14 franpb14 Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

col-12 is enough, since you are not giving other sizes

<ul class="nav nav-pills actions-menu">
<li class="nav-item">
<%= link_to organization_alliances_path(status: 'pending'), class: "nav-link #{'active' if @status == 'pending'}" do %>
<%= glyph :time %>
<%= t('organization_alliances.status.pending') %>
<% end %>
</li>
<li class="nav-item">
<%= link_to organization_alliances_path(status: 'accepted'), class: "nav-link #{'active' if @status == 'accepted'}" do %>
<%= glyph :ok %>
<%= t('organization_alliances.status.accepted') %>
<% end %>
</li>
<li class="nav-item">
<%= link_to organization_alliances_path(status: 'rejected'), class: "nav-link #{'active' if @status == 'rejected'}" do %>
<%= glyph :remove %>
<%= t('organization_alliances.status.rejected') %>
<% end %>
</li>
<li class="nav-item ms-auto">
<%= link_to organizations_path, class: "text-primary" do %>
<%= glyph :search %>
<%= t('organization_alliances.search_organizations') %>
<% end %>
</li>
</ul>
</div>
</div>

<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-body table-responsive">
<table class="table table-hover table-sm">
<thead>
<tr>
<th><%= t('organization_alliances.organization') %></th>
<th><%= t('organization_alliances.city') %></th>
<th><%= t('organization_alliances.members') %></th>
<th><%= t('organization_alliances.type') %></th>
<% if @status != 'rejected' %>
<th><%= t('organization_alliances.actions') %></th>
<% end %>
</tr>
</thead>
<tbody>
<% @alliances.each do |alliance| %>
<% other_org = alliance_recipient(alliance) %>
<tr>
<td><%= link_to other_org.name, other_org %></td>
<td><%= other_org.city %></td>
<td><%= other_org.members.count %></td>
<td>
<%= t("organization_alliances.#{alliance_initiator?(alliance) ? 'sent' : 'received'}") %>
</td>
<% if @status == 'pending' %>
<td>
<% if alliance_initiator?(alliance) %>
<%= link_to t('organization_alliances.cancel_request'),
organization_alliance_path(alliance),
method: :delete,
class: 'btn btn-danger',
data: { confirm: t('organization_alliances.confirm_cancel') } %>
<% else %>
<div>
<%= link_to t('organization_alliances.accept'),
organization_alliance_path(alliance, status: 'accepted'),
method: :put,
class: 'btn btn-primary' %>
<%= link_to t('organization_alliances.reject'),
organization_alliance_path(alliance, status: 'rejected'),
method: :put,
class: 'btn btn-danger' %>
</div>
<% end %>
</td>
<% elsif @status == 'accepted' %>
<td>
<%= link_to t('organization_alliances.end_alliance'),
organization_alliance_path(alliance),
method: :delete,
class: 'btn btn-danger',
data: { confirm: t('organization_alliances.confirm_end') } %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
</div>
16 changes: 16 additions & 0 deletions app/views/organizations/_alliance_button.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<% if current_user&.manages?(current_organization) && organization != current_organization %>
<% alliance = current_organization.alliance_with(organization) %>
<% if alliance.nil? %>
<%= link_to t('organization_alliances.request_alliance'),
organization_alliances_path(organization_alliance: { target_organization_id: organization.id }),
method: :post,
class: 'btn btn-secondary',
aria: { label: t('organization_alliances.request_alliance_for', org: organization.name) } %>
<% elsif alliance.pending? %>
<span class="badge rounded-pill bg-secondary"><%= t('organization_alliances.pending') %></span>
<% elsif alliance.accepted? %>
<span class="badge rounded-pill bg-primary"><%= t('organization_alliances.active') %></span>
<% elsif alliance.rejected? %>
<span class="badge rounded-pill bg-danger"><%= t('organization_alliances.rejected') %></span>
<% end %>
<% end %>
5 changes: 5 additions & 0 deletions app/views/organizations/_organizations_row.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@
<td>
<%= render "organizations/petition_button", organization: org %>
</td>
<% if current_user&.manages?(current_organization) %>
<td>
<%= render "organizations/alliance_button", organization: org %>
</td>
<% end %>
</tr>
5 changes: 4 additions & 1 deletion app/views/organizations/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
<th><%= t '.neighborhood' %></th>
<th><%= t '.web' %></th>
<th><%= t '.member_count' %></th>
<th></th>
<th><%= t '.membership' %></th>
<% if current_user&.manages?(current_organization) %>
<th><%= t '.alliance' %></th>
<% end %>
</tr>
</thead>
<tbody>
Expand Down
Loading