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

Api for expense summary #1691

Open
wants to merge 6 commits into
base: develop
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

class InternalApi::V1::Expenses::ExpensesSummaryController < InternalApi::V1::ApplicationController
def index
authorize :index, policy_class: Expenses::ExpensesSummaryPolicy
render json: {
expenses_summary: {
default_categories_sum: formatted_expenses_sum(default_category_expenses),
company_categories_sum: formatted_expenses_sum(company_expenses_categories),
total_sum: total_expenses_sum
}
}
end

private

def total_expenses_sum
total_expenses = current_company.expenses.sum(:amount)
FormatAmountService.new(current_company.base_currency, total_expenses).process
end

def default_category_expenses
Expense.includes(:expense_category).where(
expense_category: ExpenseCategory.default_categories,
company_id: current_company.id)
end

def company_expenses_categories
Expense.includes(:expense_category).where(expense_category: current_company.expense_categories)
end

def formatted_expenses_sum(expenses)
category_expense_sum = Hash.new(0)
expenses.each do |expense|
category_expense_sum[expense.expense_category.name] += expense.amount
end

formatted_sum = {}
category_expense_sum.each do |category, amount|
formatted_sum[category] = FormatAmountService.new(current_company.base_currency, amount).process
end
formatted_sum
end
end
9 changes: 9 additions & 0 deletions app/policies/expenses/expenses_summary_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module Expenses
class ExpensesSummaryPolicy < ApplicationPolicy
def index?
user_owner_role? || user_admin_role? || user_book_keeper_role?
end
end
end
5 changes: 5 additions & 0 deletions config/routes/internal_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@

resources :vendors, only: [:create]
resources :expense_categories, only: [:create]

namespace :expenses do
resources :expenses_summary, only: [:index]
end

resources :expenses, only: [:create, :index, :show, :update, :destroy]
resources :bulk_previous_employments, only: [:update]

Expand Down
32 changes: 32 additions & 0 deletions spec/policies/expenses/expenses_summary_policy_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe Expenses::ExpensesSummaryPolicy, type: :policy do
let(:company) { create(:company) }
let(:admin) { create(:user, current_workspace_id: company.id) }
let(:employee) { create(:user, current_workspace_id: company.id) }
let(:owner) { create(:user, current_workspace_id: company.id) }
let(:book_keeper) { create(:user, current_workspace_id: company.id) }

subject { described_class }

before do
owner.add_role :owner, company
admin.add_role :admin, company
employee.add_role :employee, company
book_keeper.add_role :book_keeper, company
end

permissions :index? do
it "grants permission to an admin, owner and bookkeeper" do
expect(subject).to permit(owner)
expect(subject).to permit(admin)
expect(subject).to permit(book_keeper)
end

it "does not grants Invoice#index permission to an employee" do
expect(subject).not_to permit(employee)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe "InternalApi::V1::Expenses::ExpensesSummary#index", type: :request do
let(:company) { create(:company, base_currency: "USD") }
let(:expense_category) { create(:expense_category, default: true) }
let(:expense_category_2) { create(:expense_category, company:) }

let(:vendor) { create(:vendor, company:) }
let(:expense1) { create(:expense, company:, expense_category:, vendor:) }
let(:expense2) { create(:expense, company:, expense_category: expense_category_2) }

let(:user) { create(:user, current_workspace_id: company.id) }

context "when user is admin" do
describe "#index" do
before do
create(:employment, company:, user:)
user.add_role :admin, company
sign_in user
expense1.reindex
expense2.reindex
Expense.reindex
send_request :get, internal_api_v1_expenses_expenses_summary_index_path, headers: auth_headers(user)
end

it "returns success" do
expect(response).to have_http_status(:ok)
end

it "returns expenses summary" do
expect(json_response["expenses_summary"]).to have_key("default_categories_sum")
expect(json_response["expenses_summary"]).to have_key("company_categories_sum")
expect(json_response["expenses_summary"]).to have_key("total_sum")

expect(json_response["expenses_summary"]["default_categories_sum"]).to eq(
{
expense_category.name => FormatAmountService.new(company.base_currency, expense1.amount).process
})
expect(json_response["expenses_summary"]["company_categories_sum"]).to eq(
{
expense_category_2.name => FormatAmountService.new(company.base_currency, expense2.amount).process
})
expect(json_response["expenses_summary"]["total_sum"]).to eq(
FormatAmountService.new(
company.base_currency,
expense1.amount + expense2.amount).process
)
end
end
end

context "when user is employee" do
before do
create(:employment, company:, user:)
user.add_role :employee, company
sign_in user
expense1.reindex
expense2.reindex
Expense.reindex
send_request :get, internal_api_v1_expenses_expenses_summary_index_path, headers: auth_headers(user)
end

it "is not be permitted to view expenses summary" do
expect(response).to have_http_status(:forbidden)
end
end

context "when unauthenticated" do
it "is not be permitted to view expenses" do
send_request :get, internal_api_v1_expenses_path
expect(response).to have_http_status(:unauthorized)
expect(json_response["error"]).to eq("You need to sign in or sign up before continuing.")
end
end
end
Loading