Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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,27 @@

# frozen_string_literal: true

class InternalApi::V1::Reports::ClientRevenuesController < InternalApi::V1::ApplicationController
def index
authorize :report
render :index, locals: { clients:, summary: }, status: :ok
end

private

def clients
@_clients ||= current_company.clients.order("name asc").includes(:invoices).map do |client|
client.payment_summary
end
end

def summary
total_paid_amount = clients.pluck(:paid_amount).sum
total_unpaid_amount = clients.pluck(:unpaid_amount).sum
{
total_paid_amount:,
total_unpaid_amount:,
total_revenue: total_paid_amount + total_unpaid_amount
}
end
end
10 changes: 10 additions & 0 deletions app/models/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ def register_on_stripe!
end
end

def payment_summary
status_and_amount = invoices.group(:status).sum(:amount)
status_and_amount.default = 0
{
name:,
paid_amount: status_and_amount["paid"],
unpaid_amount: status_and_amount["sent"] + status_and_amount["viewed"] + status_and_amount["overdue"]
}
end

private

def stripe_connected_account
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

json.key_format! camelize: :lower
json.deep_format_keys!
json.currency current_company.base_currency
json.clients clients do |client|
json.name client[:name]
json.paid_amount client[:paid_amount]
json.unpaid_amount client[:unpaid_amount]
json.total_amount client[:paid_amount] + client[:unpaid_amount]
end
json.summary do
json.total_paid_amount summary[:total_paid_amount]
json.total_unpaid_amount summary[:total_unpaid_amount]
json.total_revenue summary[:total_revenue]
end
2 changes: 2 additions & 0 deletions config/routes/internal_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
get :download
end
end

resources :client_revenues, only: [:index]
end

resources :workspaces, only: [:update]
Expand Down
104 changes: 104 additions & 0 deletions spec/requests/internal_api/v1/client_revenue_report/index_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe "InternalApi::V1::Reports::ClientRevenuesController::#index", type: :request do
let(:company) { create(:company) }
let(:user) { create(:user, current_workspace_id: company.id) }
let!(:client1) { create(:client, company:, name: "bob") }
let!(:client2) { create(:client, company:, name: "alpha") }
let!(:client1_sent_invoice1) { create(:invoice, client: client1, status: "sent") }
let!(:client1_sent_invoice2) { create(:invoice, client: client1, status: "sent") }
let!(:client1_viewed_invoice1) { create(:invoice, client: client1, status: "viewed") }
let!(:client1_paid_invoice1) { create(:invoice, client: client1, status: "paid") }
let!(:client1_paid_invoice2) { create(:invoice, client: client1, status: "paid") }
let!(:client2_overdue_invoice1) { create(:invoice, client: client2, status: "overdue") }

context "when user is an admin" do
before do
create(:company_user, company:, user:)
user.add_role :admin, company
sign_in user
end

context "when reports page's request is made" do
before do
@client1_paid_amount = client1_paid_invoice1.amount + client1_paid_invoice2.amount
@client1_unpaid_amount = client1_viewed_invoice1.amount + client1_sent_invoice1.amount +
client1_sent_invoice2.amount
@client2_paid_amount = 0
@client2_unpaid_amount = client2_overdue_invoice1.amount
get internal_api_v1_reports_client_revenues_path
end

it "returns the 200 http response" do
expect(response).to have_http_status(:ok)
end

it "returns the clients data in alaphabetical order with amount details" do
expected_clients =
[{
name: client2.name,
paidAmount: @client2_paid_amount,
unpaidAmount: @client2_unpaid_amount,
totalAmount: @client2_paid_amount + @client2_unpaid_amount
},
{
name: client1.name,
paidAmount: @client1_paid_amount,
unpaidAmount: @client1_unpaid_amount,
totalAmount: @client1_paid_amount + @client1_unpaid_amount
}]

expect(json_response["clients"]).to eq(JSON.parse(expected_clients.to_json))
end

it "returns the base currency" do
expect(json_response["currency"]).to eq(company.base_currency)
end

it "returns the summary of all clients" do
expected_summary = {
totalPaidAmount: @client1_paid_amount + @client2_paid_amount,
totalUnpaidAmount: @client1_unpaid_amount + @client2_unpaid_amount,
totalRevenue: @client1_paid_amount + @client2_paid_amount + @client1_unpaid_amount + @client2_unpaid_amount
}
expect(json_response["summary"]).to eq(JSON.parse(expected_summary.to_json))
end
end
end

context "when user is an employee" do
before do
create(:company_user, company:, user:)
user.add_role :employee, company
sign_in user
send_request :get, internal_api_v1_reports_client_revenues_path
end

it "is not permitted to view client revenue report" do
expect(response).to have_http_status(:forbidden)
end
end

context "when user is a book keeper" do
before do
create(:company_user, company:, user:)
user.add_role :book_keeper, company
sign_in user
send_request :get, internal_api_v1_reports_client_revenues_path
end

it "is not permitted to view client revenue report" do
expect(response).to have_http_status(:forbidden)
end
end

context "when unauthenticated" do
it "is not permitted to view client revenue report" do
send_request :get, internal_api_v1_reports_client_revenues_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
2 changes: 1 addition & 1 deletion spec/requests/internal_api/v1/team/index_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require "rails_helper"

RSpec.describe "InternalApi::V1::Team#index", type: :request do
let(:company) { create(:company) }
let!(:company) { create(:company) }
let(:user) { create(:user, :with_avatar, current_workspace_id: company.id) }
let(:user2) { create(:user, :with_pending_invitation, current_workspace_id: company.id) }

Expand Down