hr_lite 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +33 -0
- data/MIT-LICENSE +20 -0
- data/README.md +166 -0
- data/Rakefile +3 -0
- data/app/assets/javascripts/hr_lite/geo_punch.js +61 -0
- data/app/assets/javascripts/hr_lite/mention.js +100 -0
- data/app/assets/stylesheets/hr_lite/application.css +15 -0
- data/app/assets/stylesheets/hr_lite/hr_lite.css +239 -0
- data/app/controllers/hr_lite/admin/appraisals_controller.rb +74 -0
- data/app/controllers/hr_lite/admin/attendances_controller.rb +79 -0
- data/app/controllers/hr_lite/admin/audit_logs_controller.rb +9 -0
- data/app/controllers/hr_lite/admin/base_controller.rb +16 -0
- data/app/controllers/hr_lite/admin/designation_changes_controller.rb +33 -0
- data/app/controllers/hr_lite/admin/employees_controller.rb +53 -0
- data/app/controllers/hr_lite/admin/holidays_controller.rb +70 -0
- data/app/controllers/hr_lite/admin/leadership_controller.rb +18 -0
- data/app/controllers/hr_lite/admin/leave_balances_controller.rb +41 -0
- data/app/controllers/hr_lite/admin/leave_requests_controller.rb +41 -0
- data/app/controllers/hr_lite/admin/leave_types_controller.rb +54 -0
- data/app/controllers/hr_lite/admin/office_locations_controller.rb +46 -0
- data/app/controllers/hr_lite/admin/overview_controller.rb +12 -0
- data/app/controllers/hr_lite/admin/payroll_runs_controller.rb +93 -0
- data/app/controllers/hr_lite/admin/salary_slips_controller.rb +44 -0
- data/app/controllers/hr_lite/admin/salary_structures_controller.rb +49 -0
- data/app/controllers/hr_lite/admin/settings_controller.rb +18 -0
- data/app/controllers/hr_lite/application_controller.rb +76 -0
- data/app/controllers/hr_lite/appraisals_controller.rb +18 -0
- data/app/controllers/hr_lite/attendance_controller.rb +37 -0
- data/app/controllers/hr_lite/calendar_controller.rb +20 -0
- data/app/controllers/hr_lite/career_controller.rb +9 -0
- data/app/controllers/hr_lite/employee_profiles_controller.rb +9 -0
- data/app/controllers/hr_lite/holidays_controller.rb +9 -0
- data/app/controllers/hr_lite/home_controller.rb +7 -0
- data/app/controllers/hr_lite/kudos_controller.rb +36 -0
- data/app/controllers/hr_lite/leave_balances_controller.rb +11 -0
- data/app/controllers/hr_lite/leave_requests_controller.rb +56 -0
- data/app/controllers/hr_lite/salary_slips_controller.rb +39 -0
- data/app/controllers/hr_lite/users_controller.rb +9 -0
- data/app/helpers/hr_lite/application_helper.rb +105 -0
- data/app/jobs/hr_lite/application_job.rb +4 -0
- data/app/jobs/hr_lite/daily_digest_job.rb +33 -0
- data/app/jobs/hr_lite/leave_year_rollover_job.rb +29 -0
- data/app/mailers/hr_lite/application_mailer.rb +5 -0
- data/app/mailers/hr_lite/event_mailer.rb +33 -0
- data/app/models/concerns/hr_lite/audited.rb +70 -0
- data/app/models/concerns/hr_lite/encrypted_money.rb +32 -0
- data/app/models/hr_lite/application_record.rb +5 -0
- data/app/models/hr_lite/appraisal.rb +89 -0
- data/app/models/hr_lite/attendance_record.rb +41 -0
- data/app/models/hr_lite/audit_log.rb +17 -0
- data/app/models/hr_lite/designation_change.rb +52 -0
- data/app/models/hr_lite/employee_profile.rb +72 -0
- data/app/models/hr_lite/holiday.rb +18 -0
- data/app/models/hr_lite/kudo.rb +54 -0
- data/app/models/hr_lite/kudo_mention.rb +8 -0
- data/app/models/hr_lite/leave_balance.rb +42 -0
- data/app/models/hr_lite/leave_request.rb +207 -0
- data/app/models/hr_lite/leave_type.rb +22 -0
- data/app/models/hr_lite/office_location.rb +21 -0
- data/app/models/hr_lite/payroll_run.rb +112 -0
- data/app/models/hr_lite/salary_slip.rb +78 -0
- data/app/models/hr_lite/salary_structure.rb +49 -0
- data/app/models/hr_lite/setting.rb +14 -0
- data/app/services/hr_lite/attendance_puncher.rb +103 -0
- data/app/services/hr_lite/attendance_summary.rb +98 -0
- data/app/services/hr_lite/calculators/esi.rb +25 -0
- data/app/services/hr_lite/calculators/pf.rb +32 -0
- data/app/services/hr_lite/calculators/professional_tax.rb +20 -0
- data/app/services/hr_lite/calculators/proration.rb +30 -0
- data/app/services/hr_lite/calculators/tds.rb +74 -0
- data/app/services/hr_lite/day_status.rb +49 -0
- data/app/services/hr_lite/leave_day_counter.rb +24 -0
- data/app/services/hr_lite/overview_query.rb +39 -0
- data/app/services/hr_lite/payroll_run_processor.rb +46 -0
- data/app/services/hr_lite/pdf_renderer.rb +23 -0
- data/app/services/hr_lite/slip_builder.rb +106 -0
- data/app/services/hr_lite/working_calendar.rb +41 -0
- data/app/views/hr_lite/admin/appraisals/_form.html.erb +29 -0
- data/app/views/hr_lite/admin/appraisals/edit.html.erb +6 -0
- data/app/views/hr_lite/admin/appraisals/new.html.erb +6 -0
- data/app/views/hr_lite/admin/attendances/index.html.erb +49 -0
- data/app/views/hr_lite/admin/attendances/show.html.erb +55 -0
- data/app/views/hr_lite/admin/audit_logs/index.html.erb +39 -0
- data/app/views/hr_lite/admin/designation_changes/new.html.erb +19 -0
- data/app/views/hr_lite/admin/employees/_form.html.erb +40 -0
- data/app/views/hr_lite/admin/employees/edit.html.erb +3 -0
- data/app/views/hr_lite/admin/employees/index.html.erb +40 -0
- data/app/views/hr_lite/admin/employees/new.html.erb +3 -0
- data/app/views/hr_lite/admin/employees/show.html.erb +75 -0
- data/app/views/hr_lite/admin/holidays/index.html.erb +59 -0
- data/app/views/hr_lite/admin/leave_balances/index.html.erb +58 -0
- data/app/views/hr_lite/admin/leave_requests/index.html.erb +35 -0
- data/app/views/hr_lite/admin/leave_requests/show.html.erb +39 -0
- data/app/views/hr_lite/admin/leave_types/_form.html.erb +27 -0
- data/app/views/hr_lite/admin/leave_types/edit.html.erb +10 -0
- data/app/views/hr_lite/admin/leave_types/index.html.erb +39 -0
- data/app/views/hr_lite/admin/leave_types/new.html.erb +3 -0
- data/app/views/hr_lite/admin/office_locations/_form.html.erb +18 -0
- data/app/views/hr_lite/admin/office_locations/edit.html.erb +10 -0
- data/app/views/hr_lite/admin/office_locations/index.html.erb +30 -0
- data/app/views/hr_lite/admin/office_locations/new.html.erb +3 -0
- data/app/views/hr_lite/admin/overview/index.html.erb +72 -0
- data/app/views/hr_lite/admin/payroll_runs/index.html.erb +33 -0
- data/app/views/hr_lite/admin/payroll_runs/new.html.erb +20 -0
- data/app/views/hr_lite/admin/payroll_runs/show.html.erb +76 -0
- data/app/views/hr_lite/admin/salary_slips/show.html.erb +42 -0
- data/app/views/hr_lite/admin/salary_structures/_form.html.erb +25 -0
- data/app/views/hr_lite/admin/salary_structures/edit.html.erb +6 -0
- data/app/views/hr_lite/admin/salary_structures/new.html.erb +6 -0
- data/app/views/hr_lite/admin/settings/edit.html.erb +18 -0
- data/app/views/hr_lite/appraisals/index.html.erb +22 -0
- data/app/views/hr_lite/appraisals/show.html.erb +32 -0
- data/app/views/hr_lite/attendance/_month_grid.html.erb +41 -0
- data/app/views/hr_lite/attendance/_punch_card.html.erb +41 -0
- data/app/views/hr_lite/attendance/show.html.erb +22 -0
- data/app/views/hr_lite/calendar/show.html.erb +45 -0
- data/app/views/hr_lite/career/show.html.erb +49 -0
- data/app/views/hr_lite/employee_profiles/show.html.erb +22 -0
- data/app/views/hr_lite/event_mailer/event.html.erb +10 -0
- data/app/views/hr_lite/event_mailer/event.text.erb +10 -0
- data/app/views/hr_lite/event_mailer/leadership.html.erb +27 -0
- data/app/views/hr_lite/event_mailer/leadership.text.erb +16 -0
- data/app/views/hr_lite/holidays/_next_holiday.html.erb +12 -0
- data/app/views/hr_lite/holidays/index.html.erb +30 -0
- data/app/views/hr_lite/home/index.html.erb +25 -0
- data/app/views/hr_lite/kudos/_kudo.html.erb +17 -0
- data/app/views/hr_lite/kudos/index.html.erb +39 -0
- data/app/views/hr_lite/leave_balances/index.html.erb +19 -0
- data/app/views/hr_lite/leave_requests/_balance_chips.html.erb +17 -0
- data/app/views/hr_lite/leave_requests/index.html.erb +37 -0
- data/app/views/hr_lite/leave_requests/new.html.erb +36 -0
- data/app/views/hr_lite/leave_requests/show.html.erb +25 -0
- data/app/views/hr_lite/salary_slips/_slip_detail.html.erb +48 -0
- data/app/views/hr_lite/salary_slips/index.html.erb +29 -0
- data/app/views/hr_lite/salary_slips/pdf.html.erb +72 -0
- data/app/views/hr_lite/salary_slips/show.html.erb +12 -0
- data/app/views/hr_lite/shared/_form_errors.html.erb +10 -0
- data/app/views/hr_lite/shared/_pagination.html.erb +11 -0
- data/app/views/layouts/hr_lite/application.html.erb +56 -0
- data/app/views/layouts/hr_lite/mailer.html.erb +30 -0
- data/app/views/layouts/hr_lite/mailer.text.erb +6 -0
- data/app/views/layouts/hr_lite/pdf.html.erb +26 -0
- data/config/routes.rb +57 -0
- data/db/migrate/20260718190726_create_hr_lite_kudos.rb +14 -0
- data/db/migrate/20260718190727_create_hr_lite_kudo_mentions.rb +12 -0
- data/db/migrate/20260718190728_create_hr_lite_audit_logs.rb +15 -0
- data/db/migrate/20260718192342_create_hr_lite_office_locations.rb +13 -0
- data/db/migrate/20260718192343_create_hr_lite_attendance_records.rb +27 -0
- data/db/migrate/20260718193045_create_hr_lite_holidays.rb +14 -0
- data/db/migrate/20260718193046_create_hr_lite_leave_types.rb +20 -0
- data/db/migrate/20260718193047_create_hr_lite_leave_balances.rb +17 -0
- data/db/migrate/20260718193048_create_hr_lite_leave_requests.rb +23 -0
- data/db/migrate/20260718193049_create_hr_lite_settings.rb +10 -0
- data/db/migrate/20260718194534_create_hr_lite_employee_profiles.rb +27 -0
- data/db/migrate/20260718194535_create_hr_lite_salary_structures.rb +23 -0
- data/db/migrate/20260718194536_create_hr_lite_payroll_runs.rb +20 -0
- data/db/migrate/20260718194537_create_hr_lite_salary_slips.rb +28 -0
- data/db/migrate/20260718195953_create_hr_lite_appraisals.rb +22 -0
- data/db/migrate/20260718195954_create_hr_lite_designation_changes.rb +17 -0
- data/lib/generators/hr_lite/install/install_generator.rb +29 -0
- data/lib/generators/hr_lite/install/templates/AFTER_INSTALL +35 -0
- data/lib/generators/hr_lite/install/templates/initializer.rb +45 -0
- data/lib/hr_lite/configuration.rb +60 -0
- data/lib/hr_lite/current.rb +7 -0
- data/lib/hr_lite/engine.rb +39 -0
- data/lib/hr_lite/geo.rb +22 -0
- data/lib/hr_lite/mention_parser.rb +18 -0
- data/lib/hr_lite/money.rb +33 -0
- data/lib/hr_lite/notifications.rb +99 -0
- data/lib/hr_lite/seeds.rb +42 -0
- data/lib/hr_lite/statutory_rate_card.rb +60 -0
- data/lib/hr_lite/version.rb +3 -0
- data/lib/hr_lite.rb +104 -0
- data/lib/tasks/hr_lite_tasks.rake +8 -0
- metadata +250 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<title><%= [ content_for(:page_title), "Earthly HR" ].compact.join(" · ") %></title>
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
6
|
+
<%= csrf_meta_tags %>
|
|
7
|
+
<%= csp_meta_tag %>
|
|
8
|
+
<%= yield :head %>
|
|
9
|
+
<%= stylesheet_link_tag "hr_lite/hr_lite", media: "all" %>
|
|
10
|
+
<% HrLite.config.extra_stylesheets.each do |sheet| %>
|
|
11
|
+
<%= stylesheet_link_tag sheet, media: "all" %>
|
|
12
|
+
<% end %>
|
|
13
|
+
</head>
|
|
14
|
+
<body class="hrl-body">
|
|
15
|
+
<header class="hrl-appbar">
|
|
16
|
+
<div class="hrl-appbar__inner">
|
|
17
|
+
<a class="hrl-appbar__brand" href="<%= hr_lite.root_path %>"><%= HrLite.config.company.call[:name].presence || "HR" %></a>
|
|
18
|
+
<nav class="hrl-nav hrl-nav--top" aria-label="HR">
|
|
19
|
+
<% hrl_nav_items(HrLite::ApplicationHelper::NAV_ITEMS).each do |item| %>
|
|
20
|
+
<%= hrl_nav_link(item) %>
|
|
21
|
+
<% end %>
|
|
22
|
+
<% if hr_admin? || hr_leadership? %>
|
|
23
|
+
<span class="hrl-nav__sep" aria-hidden="true"></span>
|
|
24
|
+
<% hrl_nav_items(HrLite::ApplicationHelper::ADMIN_NAV_ITEMS).each do |item| %>
|
|
25
|
+
<%= hrl_nav_link(item) %>
|
|
26
|
+
<% end %>
|
|
27
|
+
<% end %>
|
|
28
|
+
<% if hr_leadership? %>
|
|
29
|
+
<% hrl_nav_items(HrLite::ApplicationHelper::LEADERSHIP_NAV_ITEMS).each do |item| %>
|
|
30
|
+
<%= hrl_nav_link(item) %>
|
|
31
|
+
<% end %>
|
|
32
|
+
<% end %>
|
|
33
|
+
<% if HrLite.config.back_link %>
|
|
34
|
+
<a class="hrl-nav__link hrl-nav__link--back" href="<%= HrLite.config.back_link[:url] %>"><%= HrLite.config.back_link[:label] %></a>
|
|
35
|
+
<% end %>
|
|
36
|
+
</nav>
|
|
37
|
+
<span class="hrl-appbar__user"><%= hr_display_name(hr_current_user) %></span>
|
|
38
|
+
</div>
|
|
39
|
+
</header>
|
|
40
|
+
|
|
41
|
+
<main class="hrl-main">
|
|
42
|
+
<% if flash[:notice].present? %><div class="hrl-flash hrl-flash--ok" role="status"><%= flash[:notice] %></div><% end %>
|
|
43
|
+
<% if flash[:alert].present? %><div class="hrl-flash hrl-flash--warn" role="alert"><%= flash[:alert] %></div><% end %>
|
|
44
|
+
<%= yield %>
|
|
45
|
+
</main>
|
|
46
|
+
|
|
47
|
+
<nav class="hrl-tabbar" aria-label="HR sections">
|
|
48
|
+
<% hrl_nav_items(HrLite::ApplicationHelper::NAV_ITEMS).first(5).each do |item| %>
|
|
49
|
+
<%= hrl_nav_link(item) %>
|
|
50
|
+
<% end %>
|
|
51
|
+
</nav>
|
|
52
|
+
|
|
53
|
+
<%= javascript_include_tag "hr_lite/mention", defer: true %>
|
|
54
|
+
<%= yield :scripts %>
|
|
55
|
+
</body>
|
|
56
|
+
</html>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
5
|
+
<style>
|
|
6
|
+
body { margin: 0; background: #f4f5f7; font-family: -apple-system, "Segoe UI", Roboto, Arial, sans-serif; color: #111827; }
|
|
7
|
+
.wrap { max-width: 560px; margin: 0 auto; padding: 24px 16px; }
|
|
8
|
+
.card { background: #ffffff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 24px; }
|
|
9
|
+
.brand { font-size: 13px; color: #6b7280; margin-bottom: 12px; }
|
|
10
|
+
h1 { font-size: 18px; margin: 0 0 12px; }
|
|
11
|
+
p { font-size: 14px; line-height: 1.55; }
|
|
12
|
+
.lines { margin: 16px 0; padding: 0; }
|
|
13
|
+
.lines li { font-size: 14px; margin: 4px 0 4px 18px; }
|
|
14
|
+
.diff { width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 13px; }
|
|
15
|
+
.diff th, .diff td { text-align: left; border-bottom: 1px solid #e5e7eb; padding: 6px 8px; }
|
|
16
|
+
.diff th { color: #6b7280; font-weight: 600; }
|
|
17
|
+
.btn { display: inline-block; background: #2563eb; color: #ffffff !important; padding: 10px 18px; border-radius: 8px; text-decoration: none; font-size: 14px; font-weight: 600; margin-top: 8px; }
|
|
18
|
+
.foot { font-size: 12px; color: #6b7280; margin-top: 16px; }
|
|
19
|
+
</style>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<div class="wrap">
|
|
23
|
+
<div class="card">
|
|
24
|
+
<div class="brand"><%= HrLite.config.company.call[:name].presence || "HR" %></div>
|
|
25
|
+
<%= yield %>
|
|
26
|
+
<p class="foot">Sent by the HR system. Do not reply to this email.</p>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
5
|
+
<style>
|
|
6
|
+
body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; color: #111; font-size: 12px; margin: 0; }
|
|
7
|
+
.pdf-card { border: 1px solid #ddd; border-radius: 10px; padding: 18px; margin-bottom: 14px; }
|
|
8
|
+
.pdf-head { display: flex; justify-content: space-between; align-items: flex-start; }
|
|
9
|
+
.pdf-company { font-size: 16px; font-weight: 700; }
|
|
10
|
+
.pdf-muted { color: #666; }
|
|
11
|
+
h1 { font-size: 15px; margin: 0 0 4px; }
|
|
12
|
+
table { width: 100%; border-collapse: collapse; margin: 8px 0; }
|
|
13
|
+
th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid #e5e5e5; }
|
|
14
|
+
th { font-size: 10px; text-transform: uppercase; letter-spacing: .04em; color: #666; }
|
|
15
|
+
td.num, th.num { text-align: right; }
|
|
16
|
+
tr.total td { font-weight: 700; border-top: 2px solid #111; }
|
|
17
|
+
.pdf-net { background: #f0f7f2; border-radius: 8px; padding: 12px 16px; font-size: 14px; font-weight: 700; }
|
|
18
|
+
.pdf-foot { font-size: 9px; color: #888; margin-top: 16px; }
|
|
19
|
+
.cols { display: flex; gap: 14px; }
|
|
20
|
+
.cols > div { flex: 1; }
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<%= yield %>
|
|
25
|
+
</body>
|
|
26
|
+
</html>
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
HrLite::Engine.routes.draw do
|
|
2
|
+
root "home#index"
|
|
3
|
+
|
|
4
|
+
resources :kudos, only: %i[index create destroy]
|
|
5
|
+
get "users/search", to: "users#search"
|
|
6
|
+
|
|
7
|
+
resource :attendance, only: :show, controller: "attendance" do
|
|
8
|
+
post :check_in
|
|
9
|
+
post :check_out
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
resources :leave_requests, only: %i[index show new create] do
|
|
13
|
+
member { post :cancel }
|
|
14
|
+
end
|
|
15
|
+
resources :leave_balances, only: :index
|
|
16
|
+
resources :holidays, only: :index
|
|
17
|
+
get "calendar", to: "calendar#show"
|
|
18
|
+
resources :salary_slips, only: %i[index show]
|
|
19
|
+
resource :employee_profile, only: :show, path: "profile"
|
|
20
|
+
get "career", to: "career#show"
|
|
21
|
+
resources :appraisals, only: %i[index show]
|
|
22
|
+
|
|
23
|
+
namespace :admin do
|
|
24
|
+
get "overview", to: "overview#index"
|
|
25
|
+
resources :attendances, only: %i[index show update], param: :user_id
|
|
26
|
+
resources :leave_requests, only: %i[index show] do
|
|
27
|
+
member { post :approve; post :reject }
|
|
28
|
+
end
|
|
29
|
+
resources :leave_balances, only: :index do
|
|
30
|
+
collection { post :adjust }
|
|
31
|
+
end
|
|
32
|
+
resources :leave_types, except: :show
|
|
33
|
+
resources :office_locations, except: :show
|
|
34
|
+
resources :holidays, only: %i[index create update destroy] do
|
|
35
|
+
collection { post :bulk_create }
|
|
36
|
+
end
|
|
37
|
+
resources :employees, except: :destroy do
|
|
38
|
+
resources :salary_structures, only: %i[new create edit update]
|
|
39
|
+
resources :appraisals, only: %i[new create edit update destroy] do
|
|
40
|
+
member { post :share }
|
|
41
|
+
end
|
|
42
|
+
resources :designation_changes, only: %i[new create]
|
|
43
|
+
end
|
|
44
|
+
resources :payroll_runs, only: %i[index show new create destroy] do
|
|
45
|
+
member do
|
|
46
|
+
post :compute
|
|
47
|
+
post :finalize
|
|
48
|
+
post :unlock
|
|
49
|
+
post :publish
|
|
50
|
+
get :register
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
resources :salary_slips, only: %i[show update]
|
|
54
|
+
resource :setting, only: %i[edit update]
|
|
55
|
+
resources :audit_logs, only: :index
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class CreateHrLiteKudos < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_kudos do |t|
|
|
4
|
+
# No FK to the host user table — its name is host-specific.
|
|
5
|
+
t.bigint :giver_id, null: false
|
|
6
|
+
t.string :badge
|
|
7
|
+
t.text :message, null: false
|
|
8
|
+
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
add_index :hr_lite_kudos, :giver_id
|
|
12
|
+
add_index :hr_lite_kudos, :created_at
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CreateHrLiteKudoMentions < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_kudo_mentions do |t|
|
|
4
|
+
t.references :kudo, null: false, foreign_key: { to_table: :hr_lite_kudos, on_delete: :cascade }
|
|
5
|
+
t.bigint :user_id, null: false
|
|
6
|
+
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
add_index :hr_lite_kudo_mentions, [ :kudo_id, :user_id ], unique: true
|
|
10
|
+
add_index :hr_lite_kudo_mentions, :user_id
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class CreateHrLiteAuditLogs < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_audit_logs do |t|
|
|
4
|
+
t.bigint :actor_id
|
|
5
|
+
t.string :action, null: false
|
|
6
|
+
t.string :subject_type, null: false
|
|
7
|
+
t.bigint :subject_id, null: false
|
|
8
|
+
t.json :audited_changes, null: false, default: {}
|
|
9
|
+
|
|
10
|
+
t.datetime :created_at, null: false
|
|
11
|
+
end
|
|
12
|
+
add_index :hr_lite_audit_logs, [ :subject_type, :subject_id ]
|
|
13
|
+
add_index :hr_lite_audit_logs, :created_at
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateHrLiteOfficeLocations < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_office_locations do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.decimal :lat, precision: 9, scale: 6, null: false
|
|
6
|
+
t.decimal :lng, precision: 9, scale: 6, null: false
|
|
7
|
+
t.integer :radius_m, null: false, default: 200
|
|
8
|
+
t.boolean :active, null: false, default: true
|
|
9
|
+
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class CreateHrLiteAttendanceRecords < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_attendance_records do |t|
|
|
4
|
+
t.bigint :user_id, null: false
|
|
5
|
+
t.date :date, null: false
|
|
6
|
+
t.datetime :check_in_at
|
|
7
|
+
t.datetime :check_out_at
|
|
8
|
+
t.decimal :check_in_lat, precision: 9, scale: 6
|
|
9
|
+
t.decimal :check_in_lng, precision: 9, scale: 6
|
|
10
|
+
t.integer :check_in_accuracy_m
|
|
11
|
+
t.decimal :check_out_lat, precision: 9, scale: 6
|
|
12
|
+
t.decimal :check_out_lng, precision: 9, scale: 6
|
|
13
|
+
t.integer :check_out_accuracy_m
|
|
14
|
+
t.string :status, null: false, default: "present"
|
|
15
|
+
t.boolean :flagged, null: false, default: false
|
|
16
|
+
t.string :flag_note
|
|
17
|
+
t.bigint :regularized_by_id
|
|
18
|
+
t.datetime :regularized_at
|
|
19
|
+
t.text :regularization_note
|
|
20
|
+
|
|
21
|
+
t.timestamps
|
|
22
|
+
end
|
|
23
|
+
add_index :hr_lite_attendance_records, [ :user_id, :date ], unique: true
|
|
24
|
+
add_index :hr_lite_attendance_records, :date
|
|
25
|
+
add_index :hr_lite_attendance_records, [ :date, :flagged ]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class CreateHrLiteHolidays < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_holidays do |t|
|
|
4
|
+
t.date :date, null: false
|
|
5
|
+
t.string :name, null: false
|
|
6
|
+
# Optional/restricted holidays render on the calendar but do NOT
|
|
7
|
+
# count as working-day exclusions.
|
|
8
|
+
t.boolean :optional, null: false, default: false
|
|
9
|
+
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
add_index :hr_lite_holidays, :date, unique: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class CreateHrLiteLeaveTypes < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_leave_types do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.string :code, null: false
|
|
6
|
+
t.string :color, null: false, default: "#0ea5e9"
|
|
7
|
+
t.boolean :paid, null: false, default: true
|
|
8
|
+
# nil quota = unlimited (LWP).
|
|
9
|
+
t.decimal :annual_quota, precision: 4, scale: 1
|
|
10
|
+
t.string :accrual, null: false, default: "yearly_upfront"
|
|
11
|
+
t.decimal :carry_forward_cap, precision: 4, scale: 1, null: false, default: 0
|
|
12
|
+
t.boolean :active, null: false, default: true
|
|
13
|
+
t.integer :position, null: false, default: 0
|
|
14
|
+
|
|
15
|
+
t.timestamps
|
|
16
|
+
end
|
|
17
|
+
add_index :hr_lite_leave_types, :code, unique: true
|
|
18
|
+
add_index :hr_lite_leave_types, :name, unique: true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class CreateHrLiteLeaveBalances < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_leave_balances do |t|
|
|
4
|
+
t.bigint :user_id, null: false
|
|
5
|
+
t.references :leave_type, null: false, foreign_key: { to_table: :hr_lite_leave_types }
|
|
6
|
+
t.integer :year, null: false
|
|
7
|
+
# Only carry-in and manual adjustments are materialized; entitlement
|
|
8
|
+
# and usage are computed live so they self-heal.
|
|
9
|
+
t.decimal :carried_forward, precision: 4, scale: 1, null: false, default: 0
|
|
10
|
+
t.decimal :adjustment, precision: 5, scale: 1, null: false, default: 0
|
|
11
|
+
t.string :adjustment_note
|
|
12
|
+
|
|
13
|
+
t.timestamps
|
|
14
|
+
end
|
|
15
|
+
add_index :hr_lite_leave_balances, [ :user_id, :leave_type_id, :year ], unique: true
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class CreateHrLiteLeaveRequests < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_leave_requests do |t|
|
|
4
|
+
t.bigint :user_id, null: false
|
|
5
|
+
t.references :leave_type, null: false, foreign_key: { to_table: :hr_lite_leave_types }
|
|
6
|
+
t.date :start_date, null: false
|
|
7
|
+
t.date :end_date, null: false
|
|
8
|
+
t.boolean :half_day, null: false, default: false
|
|
9
|
+
t.text :reason
|
|
10
|
+
t.string :status, null: false, default: "pending"
|
|
11
|
+
# Display cache; balance math always recomputes live.
|
|
12
|
+
t.decimal :days_count, precision: 4, scale: 1, null: false
|
|
13
|
+
t.bigint :decided_by_id
|
|
14
|
+
t.datetime :decided_at
|
|
15
|
+
t.text :decision_note
|
|
16
|
+
|
|
17
|
+
t.timestamps
|
|
18
|
+
end
|
|
19
|
+
add_index :hr_lite_leave_requests, [ :user_id, :status ]
|
|
20
|
+
add_index :hr_lite_leave_requests, [ :status, :start_date ]
|
|
21
|
+
add_index :hr_lite_leave_requests, [ :start_date, :end_date ]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class CreateHrLiteSettings < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
# Singleton row — engine-owned settings (weekend policy for now).
|
|
4
|
+
create_table :hr_lite_settings do |t|
|
|
5
|
+
t.string :weekend_policy, null: false, default: "sat_sun"
|
|
6
|
+
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class CreateHrLiteEmployeeProfiles < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_employee_profiles do |t|
|
|
4
|
+
t.bigint :user_id, null: false
|
|
5
|
+
t.string :employee_code, null: false
|
|
6
|
+
t.string :designation
|
|
7
|
+
t.date :date_of_birth
|
|
8
|
+
t.date :date_of_joining, null: false
|
|
9
|
+
t.date :date_of_exit
|
|
10
|
+
t.string :department
|
|
11
|
+
t.string :work_location
|
|
12
|
+
# Encrypted (Rails AR encryption — text columns):
|
|
13
|
+
t.text :pan_number
|
|
14
|
+
t.text :pf_uan
|
|
15
|
+
t.text :esi_number
|
|
16
|
+
t.text :bank_account_number
|
|
17
|
+
t.text :bank_ifsc
|
|
18
|
+
t.text :declared_annual_deductions
|
|
19
|
+
t.string :bank_name
|
|
20
|
+
t.string :tax_regime, null: false, default: "new"
|
|
21
|
+
|
|
22
|
+
t.timestamps
|
|
23
|
+
end
|
|
24
|
+
add_index :hr_lite_employee_profiles, :user_id, unique: true
|
|
25
|
+
add_index :hr_lite_employee_profiles, :employee_code, unique: true
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class CreateHrLiteSalaryStructures < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_salary_structures do |t|
|
|
4
|
+
t.bigint :user_id, null: false
|
|
5
|
+
# Always the 1st of a month — revisions take effect from the 1st only.
|
|
6
|
+
t.date :effective_from, null: false
|
|
7
|
+
# Encrypted money (text columns, BigDecimal via EncryptedMoney):
|
|
8
|
+
t.text :basic
|
|
9
|
+
t.text :hra
|
|
10
|
+
t.text :special_allowance
|
|
11
|
+
t.text :other_earnings
|
|
12
|
+
t.boolean :pf_applicable, null: false, default: true
|
|
13
|
+
t.boolean :pf_on_full_basic, null: false, default: false
|
|
14
|
+
t.boolean :esi_applicable, null: false, default: true
|
|
15
|
+
t.string :pt_state, null: false, default: "none"
|
|
16
|
+
t.text :notes
|
|
17
|
+
t.bigint :created_by_id
|
|
18
|
+
|
|
19
|
+
t.timestamps
|
|
20
|
+
end
|
|
21
|
+
add_index :hr_lite_salary_structures, [ :user_id, :effective_from ], unique: true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class CreateHrLitePayrollRuns < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_payroll_runs do |t|
|
|
4
|
+
t.date :period_month, null: false
|
|
5
|
+
t.string :status, null: false, default: "draft"
|
|
6
|
+
t.json :warnings, null: false, default: []
|
|
7
|
+
t.datetime :processed_at
|
|
8
|
+
t.datetime :finalized_at
|
|
9
|
+
t.datetime :published_at
|
|
10
|
+
t.bigint :created_by_id
|
|
11
|
+
t.bigint :finalized_by_id
|
|
12
|
+
t.bigint :published_by_id
|
|
13
|
+
t.text :notes
|
|
14
|
+
|
|
15
|
+
t.timestamps
|
|
16
|
+
end
|
|
17
|
+
add_index :hr_lite_payroll_runs, :period_month, unique: true
|
|
18
|
+
add_index :hr_lite_payroll_runs, :status
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class CreateHrLiteSalarySlips < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_salary_slips do |t|
|
|
4
|
+
t.references :payroll_run, null: false,
|
|
5
|
+
foreign_key: { to_table: :hr_lite_payroll_runs, on_delete: :cascade }
|
|
6
|
+
t.bigint :user_id, null: false
|
|
7
|
+
t.date :period_month, null: false
|
|
8
|
+
t.integer :days_in_month, null: false
|
|
9
|
+
t.decimal :payable_days, precision: 5, scale: 2, null: false
|
|
10
|
+
t.decimal :lop_days, precision: 5, scale: 2, null: false, default: 0
|
|
11
|
+
t.decimal :lop_override, precision: 5, scale: 2
|
|
12
|
+
# Encrypted money / encrypted JSON (text columns):
|
|
13
|
+
t.text :tds_override
|
|
14
|
+
t.text :earnings
|
|
15
|
+
t.text :deductions
|
|
16
|
+
t.text :employer_costs
|
|
17
|
+
t.text :tax_details
|
|
18
|
+
t.text :gross_earnings
|
|
19
|
+
t.text :total_deductions
|
|
20
|
+
t.text :net_pay
|
|
21
|
+
t.datetime :computed_at
|
|
22
|
+
|
|
23
|
+
t.timestamps
|
|
24
|
+
end
|
|
25
|
+
add_index :hr_lite_salary_slips, [ :payroll_run_id, :user_id ], unique: true
|
|
26
|
+
add_index :hr_lite_salary_slips, [ :user_id, :period_month ], unique: true
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class CreateHrLiteAppraisals < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :hr_lite_appraisals do |t|
|
|
4
|
+
t.bigint :user_id, null: false
|
|
5
|
+
t.bigint :reviewer_id, null: false
|
|
6
|
+
t.date :period_start, null: false
|
|
7
|
+
t.date :period_end, null: false
|
|
8
|
+
t.integer :rating
|
|
9
|
+
t.text :strengths
|
|
10
|
+
t.text :improvements
|
|
11
|
+
t.string :outcome, null: false, default: "none"
|
|
12
|
+
t.date :effective_date
|
|
13
|
+
t.string :new_designation
|
|
14
|
+
t.string :status, null: false, default: "draft"
|
|
15
|
+
t.datetime :shared_at
|
|
16
|
+
|
|
17
|
+
t.timestamps
|
|
18
|
+
end
|
|
19
|
+
add_index :hr_lite_appraisals, [ :user_id, :period_end ]
|
|
20
|
+
add_index :hr_lite_appraisals, :status
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class CreateHrLiteDesignationChanges < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
# Append-only career history.
|
|
4
|
+
create_table :hr_lite_designation_changes do |t|
|
|
5
|
+
t.bigint :user_id, null: false
|
|
6
|
+
t.string :from_designation
|
|
7
|
+
t.string :to_designation, null: false
|
|
8
|
+
t.date :effective_date, null: false
|
|
9
|
+
t.text :note
|
|
10
|
+
t.bigint :appraisal_id
|
|
11
|
+
t.bigint :created_by_id
|
|
12
|
+
|
|
13
|
+
t.timestamps
|
|
14
|
+
end
|
|
15
|
+
add_index :hr_lite_designation_changes, [ :user_id, :effective_date ]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
require "rails/generators/base"
|
|
3
|
+
|
|
4
|
+
module HrLite
|
|
5
|
+
module Generators
|
|
6
|
+
# `rails g hr_lite:install` — drops the annotated initializer and prints
|
|
7
|
+
# the remaining wiring steps. With --route it also appends a mount line.
|
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
|
10
|
+
|
|
11
|
+
class_option :route, type: :boolean, default: false,
|
|
12
|
+
desc: "Append `mount HrLite::Engine => \"/hr\"` to config/routes.rb"
|
|
13
|
+
|
|
14
|
+
def copy_initializer
|
|
15
|
+
template "initializer.rb", "config/initializers/hr_lite.rb"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add_route
|
|
19
|
+
return unless options[:route]
|
|
20
|
+
|
|
21
|
+
route 'mount HrLite::Engine => "/hr", as: :hr_lite'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def show_next_steps
|
|
25
|
+
readme "AFTER_INSTALL" if behavior == :invoke
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
hr_lite is almost ready. Remaining steps:
|
|
3
|
+
|
|
4
|
+
1. Mount it (skip if you passed --route):
|
|
5
|
+
|
|
6
|
+
# config/routes.rb — a path…
|
|
7
|
+
mount HrLite::Engine => "/hr", as: :hr_lite
|
|
8
|
+
# …or a subdomain:
|
|
9
|
+
constraints subdomain: "hr" do
|
|
10
|
+
mount HrLite::Engine => "/", as: :hr_lite
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
2. Run the migrations — they load straight from the gem, nothing to copy:
|
|
14
|
+
|
|
15
|
+
bin/rails db:migrate
|
|
16
|
+
|
|
17
|
+
(Prefer copies in your repo? `bin/rails hr_lite:install:migrations`
|
|
18
|
+
first — the engine detects the copies and steps aside.)
|
|
19
|
+
|
|
20
|
+
3. Seed leave types + fixed national holidays (idempotent, deploy-safe):
|
|
21
|
+
|
|
22
|
+
bin/rails hr_lite:seed
|
|
23
|
+
|
|
24
|
+
4. Ensure ActiveRecord encryption keys exist (payroll PII is encrypted):
|
|
25
|
+
|
|
26
|
+
bin/rails db:encryption:init # if you don't have keys yet
|
|
27
|
+
|
|
28
|
+
5. Schedule the recurring jobs in your job scheduler:
|
|
29
|
+
|
|
30
|
+
HrLite::DailyDigestJob # each morning
|
|
31
|
+
HrLite::LeaveYearRolloverJob # Jan 1
|
|
32
|
+
|
|
33
|
+
6. Review config/initializers/hr_lite.rb — set leadership emails, then
|
|
34
|
+
restart. Docs: https://github.com/kshtzkr/hr_lite/tree/main/docs
|
|
35
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# hr_lite — every host integration point in one place.
|
|
2
|
+
# Full reference: https://github.com/kshtzkr/hr_lite/blob/main/docs/CONFIGURATION.md
|
|
3
|
+
HrLite.configure do |c|
|
|
4
|
+
# Inherit your auth stack. MUST be set before boot; restart after changing.
|
|
5
|
+
c.parent_controller = "ApplicationController"
|
|
6
|
+
c.user_class = "User"
|
|
7
|
+
c.current_user_method = :current_user
|
|
8
|
+
c.authenticate_method = :authenticate_user!
|
|
9
|
+
|
|
10
|
+
# Operations tier (team attendance, leave decisions, overview board).
|
|
11
|
+
c.admin_check = ->(user) { user.respond_to?(:admin?) && user.admin? }
|
|
12
|
+
|
|
13
|
+
# Governing tier — ONLY these people change policy, employee profiles,
|
|
14
|
+
# salary structures, payroll and appraisals. Keep it in an env var so
|
|
15
|
+
# changing leadership never needs a deploy.
|
|
16
|
+
c.leadership_emails = ENV.fetch("HR_LEADERSHIP_EMAILS", "").split(",").map(&:strip)
|
|
17
|
+
|
|
18
|
+
# Where the portal is reachable (subdomain or path). Enables email link
|
|
19
|
+
# buttons and HrLite.public_url / HrLite.public_url? for deep links.
|
|
20
|
+
# c.public_url_base = "https://hr.example.com"
|
|
21
|
+
|
|
22
|
+
c.mailer_from = "hr@example.com"
|
|
23
|
+
c.company = -> { { name: "Your Company", address: nil, logo_path: nil } }
|
|
24
|
+
c.time_zone = "Asia/Kolkata"
|
|
25
|
+
|
|
26
|
+
# In-app notifications -> your bell/notification system (optional).
|
|
27
|
+
# c.notify = ->(user:, kind:, title:, body:, path:) {
|
|
28
|
+
# Notifier.notify(user, kind: kind, title: title, body: body,
|
|
29
|
+
# path: HrLite.public_url(path))
|
|
30
|
+
# }
|
|
31
|
+
|
|
32
|
+
# Salary-slip PDFs: plug your renderer, or add `gem "wicked_pdf"` +
|
|
33
|
+
# wkhtmltopdf and the built-in renderer takes over (optional).
|
|
34
|
+
# c.render_pdf = ->(template:, assigns:, cache_key:) { ... }
|
|
35
|
+
|
|
36
|
+
# Mirror promotions into your own user model (optional).
|
|
37
|
+
# c.on_designation_change = ->(user, designation) { user.update!(designation: designation) }
|
|
38
|
+
|
|
39
|
+
# Retheme via CSS variables (optional): create a stylesheet overriding
|
|
40
|
+
# --hrl-* vars and list it here.
|
|
41
|
+
# c.extra_stylesheets = [ "hr_lite_overrides" ]
|
|
42
|
+
|
|
43
|
+
# Escape hatch back to your app in the HR nav (optional).
|
|
44
|
+
# c.back_link = { label: "Back to app", url: "https://app.example.com" }
|
|
45
|
+
end
|