Skip to content

Commit

Permalink
New Line Items Entries API for Generate Invoice (#250)
Browse files Browse the repository at this point in the history
  • Loading branch information
aniketkaushik authored Apr 14, 2022
1 parent bb95c87 commit bfd9e7f
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 4 deletions.
12 changes: 12 additions & 0 deletions app/controllers/internal_api/v1/generate_invoice_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,16 @@ def index
authorize :index, policy_class: GenerateInvoicePolicy
render :index, locals: { current_company: }, status: :ok
end

def show
authorize :show, policy_class: GenerateInvoicePolicy
pagy, new_line_item_entries = pagy(client.new_line_item_entries(params[:selected_entries]), items: 10)
render json: { new_line_item_entries:, pagy: pagy_metadata(pagy) }, status: :ok
end

private

def client
@_client ||= Client.find(params[:id])
end
end
18 changes: 18 additions & 0 deletions app/models/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ class Client < ApplicationRecord
validates :email, uniqueness: { scope: :company_id }, format: { with: Devise.email_regexp }
after_discard :discard_projects

def new_line_item_entries(selected_entries)
timesheet_entries.where(bill_status: :unbilled)
.joins(
"INNER JOIN project_members ON timesheet_entries.project_id = project_members.project_id
AND timesheet_entries.user_id = project_members.user_id"
)
.joins("INNER JOIN users ON project_members.user_id = users.id")
.select(
"timesheet_entries.id as id,
users.first_name as first_name,
users.last_name as last_name,
timesheet_entries.work_date as date,
timesheet_entries.note as description,
project_members.hourly_rate as rate,
timesheet_entries.duration as qty"
).where.not(id: selected_entries)
end

def total_hours_logged(time_frame = "week")
from, to = week_month_year(time_frame)
(projects.kept.map { |project| project.timesheet_entries.where(work_date: from..to).sum(:duration) }).sum
Expand Down
4 changes: 4 additions & 0 deletions app/policies/generate_invoice_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ class GenerateInvoicePolicy < ApplicationPolicy
def index?
user_owner_or_admin?
end

def show?
user_owner_or_admin?
end
end
2 changes: 1 addition & 1 deletion config/initializers/pagy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

# Array extra: Paginate arrays efficiently, avoiding expensive array-wrapping and without overriding
# See https://ddnexus.github.io/pagy/extras/array
# require 'pagy/extras/array'
require "pagy/extras/array"

# Calendar extra: Add pagination filtering by calendar time unit (year, quarter, month, week, day)
# See https://ddnexus.github.io/pagy/extras/calendar
Expand Down
2 changes: 1 addition & 1 deletion config/routes/internal_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
resources :reports, only: [:index]
resources :workspaces, only: [:update]
resources :invoices, only: [:index]
resources :generate_invoice, only: [:index]
resources :generate_invoice, only: [:index, :show]
end
end
4 changes: 2 additions & 2 deletions db/seeds/08_timesheet_entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
(@timesheet_entry_start_date..@timesheet_entry_end_date).each do |date|
TimesheetEntry.create!(
user: project_member.user, project: project_member.project, duration: 60,
note: "Worked on #{@project_1_client_1_saeloun_india.name}", work_date: date)
note: "Worked on #{@project_1_client_1_saeloun_india.name}", bill_status: :unbilled, work_date: date)
end
end

@project_1_client_1_saeloun_us.project_members.each do |project_member|
(@timesheet_entry_start_date..@timesheet_entry_end_date).each do |date|
TimesheetEntry.create!(
user: project_member.user, project: project_member.project, duration: 60,
note: "Worked on #{@project_1_client_1_saeloun_us.name}", work_date: date)
note: "Worked on #{@project_1_client_1_saeloun_us.name}", bill_status: :unbilled, work_date: date)
end
end
# Timesheet Entry End
Expand Down
56 changes: 56 additions & 0 deletions spec/models/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,61 @@
end
end
end

describe "#new_line_item_entries" do
let(:company) { create(:company) }
let(:user) { create(:user) }
let(:client) { create(:client, company:) }
let(:project) { create(:project, client:) }
let(:project_member) { create(:project_member, project:, user:, hourly_rate: 5000) }

before do
create_list(:timesheet_entry, 5, user:, project:)
end

context "when no entries are selected" do
let(:selected_entries) { [] }

it "returns all the line item entries" do
result =
client.timesheet_entries.where(bill_status: :unbilled)
.joins("INNER JOIN project_members ON timesheet_entries.project_id = project_members.project_id
AND timesheet_entries.user_id = project_members.user_id")
.joins("INNER JOIN users ON project_members.user_id = users.id")
.select(
"timesheet_entries.id as id,
users.first_name as first_name,
users.last_name as last_name,
timesheet_entries.work_date as date,
timesheet_entries.note as description,
project_members.hourly_rate as rate,
timesheet_entries.duration as qty"
).where.not(id: selected_entries)
expect(client.new_line_item_entries(selected_entries)).to eq(result)
end
end

context "when some entries are selected" do
let(:selected_entries) { [ 1, 2 ] }

it "returns all the line item entries except the entries which are selected" do
result =
client.timesheet_entries.where(bill_status: :unbilled)
.joins("INNER JOIN project_members ON timesheet_entries.project_id = project_members.project_id
AND timesheet_entries.user_id = project_members.user_id")
.joins("INNER JOIN users ON project_members.user_id = users.id")
.select(
"timesheet_entries.id as id,
users.first_name as first_name,
users.last_name as last_name,
timesheet_entries.work_date as date,
timesheet_entries.note as description,
project_members.hourly_rate as rate,
timesheet_entries.duration as qty"
).where.not(id: selected_entries)
expect(client.new_line_item_entries(selected_entries)).to eq(result)
end
end
end
end
end
62 changes: 62 additions & 0 deletions spec/requests/internal_api/v1/generate_invoice/show_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe "InternalApi::V1::GeneratInvoice#show", type: :request do
let(:company) { create(:company) }
let(:user) { create(:user, current_workspace_id: company.id) }
let(:client) { create(:client, company:) }
let(:project) { create(:project, client:) }
let(:project_member) { create(:project_member, user:, project:, hourly_rate: 5000) }

context "when user is admin" do
before do
create(:company_user, company:, user:)
user.add_role :admin, company
sign_in user
create_list(:timesheet_entry, 5, user:, project:)
send_request :get, internal_api_v1_generate_invoice_path(client)
end

context "when no entries are selected" do
let(:selected_entries) { [] }

it "returns the all new_line_item_entries" do
new_line_item_entries = client.new_line_item_entries(selected_entries)
expect(response).to have_http_status(:ok)
expect(json_response["new_line_item_entries"]).to eq(JSON.parse(new_line_item_entries.to_json))
end
end

context "when some entries are selected" do
let(:selected_entries) { [1, 2] }

it "returns the all new_line_item_entries except the one whcih are selected" do
new_line_item_entries = client.new_line_item_entries(selected_entries)
expect(response).to have_http_status(:ok)
expect(json_response["new_line_item_entries"]).to eq(JSON.parse(new_line_item_entries.to_json))
end
end
end

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

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

context "when unauthenticated" do
it "is not permitted to view time entry report" do
send_request :get, internal_api_v1_reports_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

0 comments on commit bfd9e7f

Please sign in to comment.