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

New Line Items Entries API for Generate Invoice #250

Merged
merged 9 commits into from
Apr 14, 2022
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
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