diff --git a/Gemfile.lock b/Gemfile.lock index bbebdb49cb..5fd0194da8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -496,7 +496,8 @@ GEM actionpack (>= 5.2) railties (>= 5.2) retriable (3.1.2) - rexml (3.2.6) + rexml (3.2.8) + strscan (>= 3.0.9) rolify (6.0.1) rspec-buildkite (0.1.6) rspec-core (~> 3.0) @@ -624,6 +625,7 @@ GEM stripe (8.2.0) strong_migrations (1.4.2) activerecord (>= 5.2) + strscan (3.1.0) thor (1.3.1) tilt (2.0.11) timeout (0.4.1) diff --git a/app/controllers/internal_api/v1/custom_leaves_controller.rb b/app/controllers/internal_api/v1/custom_leaves_controller.rb new file mode 100644 index 0000000000..1a6ca7b8ad --- /dev/null +++ b/app/controllers/internal_api/v1/custom_leaves_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class InternalApi::V1::CustomLeavesController < InternalApi::V1::ApplicationController + before_action :set_leave, only: [:update] + + def update + authorize current_user, policy_class: LeaveWithLeaveTypesPolicy + CustomLeavesService.new(leave, update_params).process + render json: { notice: "Leaves updated successfully" }, status: :ok + end + + private + + def set_leave + @_leave ||= current_company.leaves.find_or_create_by(year: params[:year]) + end + + def update_params + params.require(:custom_leaves).permit( + add_custom_leaves: [:name, :color, :icon, :allocation_value, + :allocation_period, user_ids: [], + ], + update_custom_leaves: [:id, :name, :color, :icon, :allocation_value, + :allocation_period, user_ids: [], + ], + remove_custom_leaves: [] + ) + end +end diff --git a/app/controllers/internal_api/v1/employments_controller.rb b/app/controllers/internal_api/v1/employments_controller.rb index e7d1f479ef..ce391b83b5 100644 --- a/app/controllers/internal_api/v1/employments_controller.rb +++ b/app/controllers/internal_api/v1/employments_controller.rb @@ -5,7 +5,7 @@ class InternalApi::V1::EmploymentsController < InternalApi::V1::ApplicationContr def index authorize Employment - render :index, locals: { users: users_with_not_client_role }, status: :ok + render :index, locals: { users: current_company.employees_without_client_role }, status: :ok end def show @@ -28,9 +28,4 @@ def set_employment def employment_params params.require(:employment).permit(:designation, :employment_type, :joined_at, :resigned_at, :employee_id) end - - def users_with_not_client_role - users_with_client_role_ids = current_company.users.joins(:roles).where(roles: { name: "client" }).pluck(:id) - current_company.users.kept.where.not(id: users_with_client_role_ids) - end end diff --git a/app/controllers/internal_api/v1/leaves_controller.rb b/app/controllers/internal_api/v1/leaves_controller.rb index 6f05f405a7..23fe06f942 100644 --- a/app/controllers/internal_api/v1/leaves_controller.rb +++ b/app/controllers/internal_api/v1/leaves_controller.rb @@ -5,7 +5,7 @@ class InternalApi::V1::LeavesController < InternalApi::V1::ApplicationController def index authorize Leave - leaves = current_company.leaves.includes([:leave_types]) + leaves = current_company.leaves.includes([:leave_types, :custom_leaves]) render :index, locals: { leaves: } diff --git a/app/models/company.rb b/app/models/company.rb index b9b0a6e54f..4f8b528854 100644 --- a/app/models/company.rb +++ b/app/models/company.rb @@ -118,6 +118,14 @@ def billable_clients end def employees_without_client_role - users.with_kept_employments.joins(:roles).where.not(roles: { name: "client" }).distinct + user_ids_with_only_client_role = users.with_kept_employments + .joins(:roles) + .group("users.id, roles.resource_id, roles.resource_type") + .having("COUNT(roles.id) = 1 AND MAX(roles.name) = 'client' \ + AND roles.resource_id = #{id} \ + AND roles.resource_type = 'Company'") + .pluck("users.id") + + users.with_kept_employments.where.not(id: user_ids_with_only_client_role).distinct end end diff --git a/app/models/custom_leave.rb b/app/models/custom_leave.rb new file mode 100644 index 0000000000..3c49427c7f --- /dev/null +++ b/app/models/custom_leave.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: custom_leaves +# +# id :bigint not null, primary key +# allocation_period :integer not null +# allocation_value :integer not null +# name :string not null +# created_at :datetime not null +# updated_at :datetime not null +# leave_id :bigint not null +# +# Indexes +# +# index_custom_leaves_on_leave_id (leave_id) +# +# Foreign Keys +# +# fk_rails_... (leave_id => leaves.id) +# +class CustomLeave < ApplicationRecord + belongs_to :leave + has_many :custom_leave_users, dependent: :destroy + has_many :users, through: :custom_leave_users + + enum allocation_period: { + days: 0, + weeks: 1, + months: 2 + } + + validates :name, :allocation_value, :allocation_period, presence: true + validates :allocation_value, numericality: { greater_than_or_equal_to: 1 } +end diff --git a/app/models/custom_leave_user.rb b/app/models/custom_leave_user.rb new file mode 100644 index 0000000000..7c4fe8f4b9 --- /dev/null +++ b/app/models/custom_leave_user.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: custom_leave_users +# +# id :bigint not null, primary key +# created_at :datetime not null +# updated_at :datetime not null +# custom_leave_id :bigint not null +# user_id :bigint not null +# +# Indexes +# +# index_custom_leave_users_on_custom_leave_id (custom_leave_id) +# index_custom_leave_users_on_user_id (user_id) +# +# Foreign Keys +# +# fk_rails_... (custom_leave_id => custom_leaves.id) +# fk_rails_... (user_id => users.id) +# +class CustomLeaveUser < ApplicationRecord + belongs_to :custom_leave + belongs_to :user +end diff --git a/app/models/device.rb b/app/models/device.rb index b2ed741a45..223e87d46c 100644 --- a/app/models/device.rb +++ b/app/models/device.rb @@ -4,15 +4,18 @@ # # Table name: devices # -# id :bigint not null, primary key -# device_type :string default("laptop") -# name :string -# serial_number :string -# specifications :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# company_id :bigint not null -# user_id :bigint not null +# id :bigint not null, primary key +# device_type :string default("laptop") +# insurance_bought_date :date +# insurance_expiry_date :date +# is_insured :boolean default(FALSE) +# name :string +# serial_number :string +# specifications :jsonb +# created_at :datetime not null +# updated_at :datetime not null +# company_id :bigint not null +# user_id :bigint not null # # Indexes # diff --git a/app/models/invitation.rb b/app/models/invitation.rb index e3945cf4c6..d7928ef920 100644 --- a/app/models/invitation.rb +++ b/app/models/invitation.rb @@ -116,17 +116,13 @@ def recipient_email_not_changed end end - def users_not_with_client_role - company.employments.kept.joins(user: :roles).where.not(roles: { name: "client" }) - end - def send_invitation_mail user_already_exists = User.exists?(email: recipient_email) company_details = { name: company.name, logo: company.company_logo, - employee_count: users_not_with_client_role.count + employee_count: company.employees_without_client_role.count } sender_details = { diff --git a/app/models/leave.rb b/app/models/leave.rb index 5690a892b0..7c2eefdca2 100644 --- a/app/models/leave.rb +++ b/app/models/leave.rb @@ -30,6 +30,7 @@ class Leave < ApplicationRecord belongs_to :company has_many :leave_types, class_name: "LeaveType", dependent: :destroy + has_many :custom_leaves, class_name: "CustomLeave", dependent: :destroy has_many :timeoff_entries, through: :leave_types validates :year, presence: true, diff --git a/app/models/user.rb b/app/models/user.rb index 31291c2b68..f2b6b854fc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -72,6 +72,8 @@ def initialize(msg = "Spam User Login") has_many :clients, through: :projects has_many :client_members, dependent: :destroy has_many :timeoff_entries, dependent: :destroy + has_many :custom_leave_users + has_many :custom_leaves, through: :custom_leave_users, source: :custom_leave has_many :carryovers rolify strict: true diff --git a/app/services/clients/index_service.rb b/app/services/clients/index_service.rb index f2689c2bca..1fde8a7b08 100644 --- a/app/services/clients/index_service.rb +++ b/app/services/clients/index_service.rb @@ -14,8 +14,7 @@ def process { client_details:, total_minutes:, - overdue_outstanding_amount:, - users_not_in_client_members: + overdue_outstanding_amount: } end @@ -59,10 +58,5 @@ def total_minutes def overdue_outstanding_amount current_company.overdue_and_outstanding_and_draft_amount end - - def users_not_in_client_members - users_with_client_role = current_company.users.includes(:roles).where(roles: { name: "client" }) - users_with_client_role.where.not(id: current_company.client_members.pluck(:user_id)) - end end end diff --git a/app/services/custom_leaves_service.rb b/app/services/custom_leaves_service.rb new file mode 100644 index 0000000000..fb9d6a39db --- /dev/null +++ b/app/services/custom_leaves_service.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class CustomLeavesService + attr_reader :leave, :params + + def initialize(leave, params) + @leave = leave + @params = params + end + + def process + ActiveRecord::Base.transaction do + add_custom_leaves + update_custom_leaves + remove_custom_leaves + end + end + + private + + def add_custom_leaves + return if params[:add_custom_leaves].blank? + + params[:add_custom_leaves].each do |custom_leave| + leave.custom_leaves.create!(custom_leave) + end + end + + def update_custom_leaves + return if params[:update_custom_leaves].blank? + + params[:update_custom_leaves].each do |update_custom_leave| + custom_leave = leave.custom_leaves.find(update_custom_leave[:id]) + next unless custom_leave + + custom_leave.update!(update_custom_leave) + end + end + + def remove_custom_leaves + return if params[:remove_custom_leaves].blank? + + leave.custom_leaves.where(id: params[:remove_custom_leaves]).destroy_all + end +end diff --git a/app/services/reports/time_entries/report_service.rb b/app/services/reports/time_entries/report_service.rb index 9c5e39dac0..da72a3ba16 100644 --- a/app/services/reports/time_entries/report_service.rb +++ b/app/services/reports/time_entries/report_service.rb @@ -31,7 +31,7 @@ def filter_options if get_filters @_filter_options ||= { clients: current_company.clients.includes([:logo_attachment]).order(:name), - team_members: users_not_client_role.order(:first_name), + team_members: current_company.employees_without_client_role.order(:first_name), projects: current_company.projects.as_json(only: [:id, :name]) } end @@ -120,11 +120,6 @@ def active_time_entries { discarded_at: nil } end - def users_not_client_role - users_with_client_role_ids = current_company.users.joins(:roles).where(roles: { name: "client" }).pluck(:id) - current_company.users.where.not(id: users_with_client_role_ids) - end - def pagination_details { pages: @reports.total_pages, diff --git a/app/views/internal_api/v1/leaves/index.json.jbuilder b/app/views/internal_api/v1/leaves/index.json.jbuilder index e5207525a5..b80d6b913f 100644 --- a/app/views/internal_api/v1/leaves/index.json.jbuilder +++ b/app/views/internal_api/v1/leaves/index.json.jbuilder @@ -13,4 +13,14 @@ json.leaves leaves do |leave| json.allocation_frequency leave_type.allocation_frequency json.carry_forward_days leave_type.carry_forward_days end + json.custom_leaves leave.custom_leaves do |custom_leave| + json.id custom_leave.id + json.name custom_leave.name + json.allocation_value custom_leave.allocation_value + json.allocation_period custom_leave.allocation_period + json.users custom_leave.users do |user| + json.id user.id + json.full_name user.full_name + end + end end diff --git a/config/routes/internal_api.rb b/config/routes/internal_api.rb index 169dfcf185..ed64750fec 100644 --- a/config/routes/internal_api.rb +++ b/config/routes/internal_api.rb @@ -155,7 +155,7 @@ resources :timeoff_entries, except: [:new, :edit] patch "leave_with_leave_type/:year", to: "leave_with_leave_types#update", as: :update_leave_with_leave_types - + patch "custom_leaves/:year", to: "custom_leaves#update" match "*path", to: "application#not_found", via: :all, constraints: lambda { |req| req.path.exclude?("rails/active_storage") && req.path.include?("internal_api") } diff --git a/db/migrate/20240426050940_create_custom_leaves.rb b/db/migrate/20240426050940_create_custom_leaves.rb new file mode 100644 index 0000000000..eb4896589b --- /dev/null +++ b/db/migrate/20240426050940_create_custom_leaves.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateCustomLeaves < ActiveRecord::Migration[7.1] + def change + create_table :custom_leaves do |t| + t.string :name, null: false + t.integer :allocation_value, null: false + t.integer :allocation_period, null: false + t.references :leave, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/migrate/20240426053100_create_custom_leave_users.rb b/db/migrate/20240426053100_create_custom_leave_users.rb new file mode 100644 index 0000000000..c1b57d774e --- /dev/null +++ b/db/migrate/20240426053100_create_custom_leave_users.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateCustomLeaveUsers < ActiveRecord::Migration[7.1] + def change + create_table :custom_leave_users do |t| + t.references :custom_leave, null: false, foreign_key: true + t.references :user, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 0853a1da63..4e7bd6c553 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -162,6 +162,25 @@ t.boolean "calendar_enabled", default: true end + create_table "custom_leave_users", force: :cascade do |t| + t.bigint "custom_leave_id", null: false + t.bigint "user_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["custom_leave_id"], name: "index_custom_leave_users_on_custom_leave_id" + t.index ["user_id"], name: "index_custom_leave_users_on_user_id" + end + + create_table "custom_leaves", force: :cascade do |t| + t.string "name", null: false + t.integer "allocation_value", null: false + t.integer "allocation_period", null: false + t.bigint "leave_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["leave_id"], name: "index_custom_leaves_on_leave_id" + end + create_table "data_migrations", primary_key: "version", id: :string, force: :cascade do |t| end @@ -557,6 +576,9 @@ add_foreign_key "client_members", "companies" add_foreign_key "client_members", "users" add_foreign_key "clients", "companies" + add_foreign_key "custom_leave_users", "custom_leaves", column: "custom_leave_id" + add_foreign_key "custom_leave_users", "users" + add_foreign_key "custom_leaves", "leaves", column: "leave_id" add_foreign_key "devices", "companies" add_foreign_key "devices", "users" add_foreign_key "employments", "companies" diff --git a/package.json b/package.json index 293a730edd..03546e64ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@saeloun/miru-web", - "version": "1.5.3", + "version": "1.6.5", "dependencies": { "@babel/core": "^7.21.3", "@babel/plugin-proposal-private-methods": "^7.16.5",