Skip to content
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

AdditionalExpenses and ContactTopicAnswer controllers & policies #6060

Closed
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
32 changes: 32 additions & 0 deletions app/controllers/additional_expenses_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class AdditionalExpensesController < ApplicationController
def create
@additional_expense = AdditionalExpense.new(additional_expense_params)
authorize @additional_expense

respond_to do |format|
if @additional_expense.save
format.json { render json: @additional_expense.as_json, status: :created }
else
format.json { render json: @additional_expense.errors.as_json, status: :unprocessable_entity }
thejonroberts marked this conversation as resolved.
Show resolved Hide resolved
end
end
end

def destroy
@additional_expense = AdditionalExpense.find(params[:id])
authorize @additional_expense

@additional_expense.destroy!

respond_to do |format|
format.json { head :no_content }
end
end

private

def additional_expense_params
params.require(:additional_expense)
.permit(:case_contact_id, :other_expense_amount, :other_expenses_describe)
end
end
32 changes: 32 additions & 0 deletions app/controllers/contact_topic_answers_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class ContactTopicAnswersController < ApplicationController
def create
@contact_topic_answer = ContactTopicAnswer.new(contact_topic_answer_params)
authorize @contact_topic_answer

respond_to do |format|
if @contact_topic_answer.save
format.json { render json: @contact_topic_answer.as_json, status: :created }
else
format.json { render json: @contact_topic_answer.errors.as_json, status: :unprocessable_entity }
end
end
end

def destroy
@contact_topic_answer = ContactTopicAnswer.find(params[:id])
authorize @contact_topic_answer

@contact_topic_answer.destroy!

respond_to do |format|
format.json { head :no_content }
end
end

private

def contact_topic_answer_params
params.require(:contact_topic_answer)
.permit(:id, :contact_topic_id, :case_contact_id, :value, :_destroy)
end
end
12 changes: 10 additions & 2 deletions app/models/additional_expense.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
class AdditionalExpense < ApplicationRecord
belongs_to :case_contact
has_one :casa_case, through: :case_contact
has_one :casa_org, through: :casa_case

# validates :other_expense_amount, presence: true
validates :other_expenses_describe, presence: {message: "Expense description cannot be blank."}
validates :other_expenses_describe, presence: true, if: :describe_required?

alias_attribute :amount, :other_expense_amount
alias_attribute :describe, :other_expenses_describe

def describe_required?
other_expense_amount&.positive?
end
end

# == Schema Information
Expand Down
3 changes: 3 additions & 0 deletions app/models/contact_topic_answer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ class ContactTopicAnswer < ApplicationRecord
belongs_to :case_contact
belongs_to :contact_topic

has_one :casa_case, through: :case_contact
has_one :casa_org, through: :casa_case

validates :selected, inclusion: [true, false]

default_scope { joins(:contact_topic).order("contact_topics.id") }
Expand Down
27 changes: 27 additions & 0 deletions app/policies/additional_expense_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class AdditionalExpensePolicy < ApplicationPolicy
class Scope < ApplicationPolicy::Scope
def resolve
case user
when CasaAdmin, Supervisor
scope.joins([:case_contact, :casa_case]).where(casa_case: {casa_org_id: user.casa_org.id})
when Volunteer
scope.where(case_contact: user.case_contacts)
else
scope.none
end
end
end

def create?
case user
when Volunteer
user.case_contacts.include?(record.case_contact)
when CasaAdmin, Supervisor
same_org?
else
false
end
end

alias_method :destroy?, :create?
end
27 changes: 27 additions & 0 deletions app/policies/contact_topic_answer_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class ContactTopicAnswerPolicy < ApplicationPolicy
class Scope < ApplicationPolicy::Scope
def resolve
case user
when CasaAdmin, Supervisor
scope.joins([:case_contact, :casa_case]).where(casa_case: {casa_org_id: user.casa_org&.id})
when Volunteer
scope.where(case_contact: user.case_contacts)
else
scope.none
end
end
end

def create?
case user
when Volunteer
user.case_contacts.include?(record.case_contact)
when CasaAdmin, Supervisor
same_org?
else
false
end
end

alias_method :destroy?, :create?
end
3 changes: 3 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
en:
activerecord:
attributes:
additional_expense:
other_expenses_amount: Amount
other_expenses_describe: Description
case_contact:
case_contact_contact_types:
one: Contact Type
Expand Down
6 changes: 6 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@
end
end

resources :contact_topic_answers, only: %i[create destroy],
constraints: lambda { |req| req.format == :json }

resources :reports, only: %i[index]
get :export_emails, to: "reports#export_emails"

Expand Down Expand Up @@ -191,6 +194,9 @@
end
resources :case_court_orders, only: %i[destroy]

resources :additional_expenses, only: %i[create destroy],
constraints: lambda { |req| req.format == :json }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did these as root-level resources. They could be case_contact member resources (nested), as they can't exist without a contact parent. I tend to error on the side of not nesting resources... and it was easier. Can move if need be.

The constraints for :json was new to me. Made sense for the no-view / api style endpoint.

namespace :all_casa_admins do
resources :casa_orgs, only: [:new, :create, :show] do
resources :casa_admins, only: [:new, :create, :edit, :update] do
Expand Down
13 changes: 13 additions & 0 deletions spec/models/additional_expense_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,17 @@

RSpec.describe AdditionalExpense, type: :model do
it { is_expected.to belong_to(:case_contact) }
it { is_expected.to have_one(:casa_case).through(:case_contact) }
it { is_expected.to have_one(:casa_org).through(:casa_case) }

describe "validations" do
let(:case_contact) { build_stubbed :case_contact }

it "requires describe only if amount is positive" do
expense = build(:additional_expense, amount: 0, describe: nil, case_contact:)
expect(expense).to be_valid
expense.update(amount: 1)
expect(expense).to be_invalid
end
end
end
109 changes: 109 additions & 0 deletions spec/policies/additional_expense_policy_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
require "rails_helper"

RSpec.describe AdditionalExpensePolicy, type: :policy do
let(:casa_org) { create :casa_org }
let(:volunteer) { create :volunteer, :with_single_case, casa_org: }
let(:supervisor) { create :supervisor, casa_org: }
let(:casa_admin) { create :casa_admin, casa_org: }
let(:all_casa_admin) { create :all_casa_admin }

let(:casa_case) { volunteer.casa_cases.first }
let(:case_contact) { create :case_contact, casa_case:, creator: volunteer }
let!(:additional_expense) { create :additional_expense, case_contact: }

let(:same_org_volunteer) { create :volunteer, casa_org: }
let!(:same_org_volunteer_case_assignment) { create :case_assignment, volunteer: same_org_volunteer, casa_case: }

subject { described_class }

permissions :create?, :destroy? do
it "does not permit a nil user" do
expect(described_class).not_to permit(nil, additional_expense)
end

it "permits a volunteer assigned to the expense's case contact" do
expect(described_class).to permit(volunteer, additional_expense)
end

it "does not permit a volunteer who did not create the case contact" do
expect(same_org_volunteer.casa_cases).to include(casa_case)
expect(described_class).not_to permit(same_org_volunteer, additional_expense)
end

it "permits a supervisor" do
expect(described_class).to permit(supervisor, additional_expense)
end

it "does not permit a supervisor for a different casa org" do
other_org_supervisor = create :supervisor, casa_org: create(:casa_org)
expect(described_class).not_to permit(other_org_supervisor, additional_expense)
end

it "permits a casa admin" do
expect(described_class).to permit(casa_admin, additional_expense)
end

it "does not permit a casa admin for a different casa org" do
other_org_casa_admin = create :casa_admin, casa_org: create(:casa_org)
expect(described_class).not_to permit(other_org_casa_admin, additional_expense)
end

it "does not permit an all casa admin" do
expect(described_class).not_to permit(all_casa_admin, additional_expense)
end
end

describe "Scope#resolve" do
let(:same_org_volunteer_case_contact) { create :case_contact, casa_case:, creator: same_org_volunteer }
let!(:same_org_other_volunteer_additional_expense) do
create :additional_expense, case_contact: same_org_volunteer_case_contact
end

let(:other_volunteer_case_contact) { create :case_contact, casa_case:, creator: other_volunteer }
let!(:other_volunteer_additional_expense) { create :additional_expense, case_contact: other_org_case_contact }

let(:other_org) { create :casa_org }
let(:other_org_volunteer) { create :volunteer, casa_org: other_org }
let(:other_org_casa_case) { create :casa_case, casa_org: other_org }
let(:other_org_case_contact) { create :case_contact, casa_case: other_org_casa_case, creator: other_org_volunteer }
let!(:other_org_additional_expense) { create :additional_expense, case_contact: other_org_case_contact }

subject { described_class::Scope.new(user, AdditionalExpense.all).resolve }

context "when user is a visitor" do
let(:user) { nil }

it { is_expected.not_to include(additional_expense) }
it { is_expected.not_to include(other_org_additional_expense) }
end

context "when user is a volunteer" do
let(:user) { volunteer }

it { is_expected.to include(additional_expense) }
it { is_expected.not_to include(other_volunteer_additional_expense) }
it { is_expected.not_to include(other_org_additional_expense) }
end

context "when user is a supervisor" do
let(:user) { supervisor }

it { is_expected.to include(additional_expense) }
it { is_expected.not_to include(other_org_additional_expense) }
end

context "when user is a casa_admin" do
let(:user) { casa_admin }

it { is_expected.to include(additional_expense) }
it { is_expected.not_to include(other_org_additional_expense) }
end

context "when user is an all_casa_admin" do
let(:user) { all_casa_admin }

it { is_expected.not_to include(additional_expense) }
it { is_expected.not_to include(other_org_additional_expense) }
end
end
end
Loading