hr_lite 0.4.0 → 0.5.2

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +173 -1
  3. data/README.md +312 -99
  4. data/app/assets/javascripts/hr_lite/confirm.js +31 -0
  5. data/app/assets/stylesheets/hr_lite/hr_lite.css +28 -0
  6. data/app/controllers/hr_lite/admin/appraisals_controller.rb +1 -1
  7. data/app/controllers/hr_lite/admin/attendances_controller.rb +16 -1
  8. data/app/controllers/hr_lite/admin/audit_logs_controller.rb +4 -1
  9. data/app/controllers/hr_lite/admin/designation_changes_controller.rb +1 -1
  10. data/app/controllers/hr_lite/admin/employees_controller.rb +37 -13
  11. data/app/controllers/hr_lite/admin/leave_balances_controller.rb +15 -8
  12. data/app/controllers/hr_lite/admin/leave_requests_controller.rb +16 -0
  13. data/app/controllers/hr_lite/admin/payroll_runs_controller.rb +1 -1
  14. data/app/controllers/hr_lite/admin/resignations_controller.rb +17 -5
  15. data/app/controllers/hr_lite/admin/salary_slips_controller.rb +18 -3
  16. data/app/controllers/hr_lite/admin/salary_structures_controller.rb +1 -1
  17. data/app/controllers/hr_lite/admin/settings_controller.rb +1 -1
  18. data/app/controllers/hr_lite/admin/superadmin_controller.rb +21 -0
  19. data/app/controllers/hr_lite/application_controller.rb +5 -1
  20. data/app/controllers/hr_lite/org_controller.rb +6 -1
  21. data/app/controllers/hr_lite/resignations_controller.rb +6 -1
  22. data/app/helpers/hr_lite/application_helper.rb +7 -13
  23. data/app/jobs/hr_lite/application_job.rb +5 -0
  24. data/app/jobs/hr_lite/daily_digest_job.rb +1 -1
  25. data/app/jobs/hr_lite/leave_year_rollover_job.rb +1 -1
  26. data/app/jobs/hr_lite/payroll_auto_draft_job.rb +5 -2
  27. data/app/models/concerns/hr_lite/audited.rb +11 -4
  28. data/app/models/hr_lite/appraisal.rb +9 -1
  29. data/app/models/hr_lite/audit_log.rb +11 -0
  30. data/app/models/hr_lite/comp_off_request.rb +5 -15
  31. data/app/models/hr_lite/designation_change.rb +18 -1
  32. data/app/models/hr_lite/employee_profile.rb +26 -1
  33. data/app/models/hr_lite/leave_balance.rb +51 -5
  34. data/app/models/hr_lite/leave_request.rb +9 -1
  35. data/app/models/hr_lite/leave_type.rb +13 -3
  36. data/app/models/hr_lite/payroll_run.rb +39 -10
  37. data/app/models/hr_lite/regularization_request.rb +8 -0
  38. data/app/models/hr_lite/resignation.rb +27 -0
  39. data/app/models/hr_lite/salary_slip.rb +7 -3
  40. data/app/models/hr_lite/setting.rb +2 -0
  41. data/app/services/hr_lite/attendance_puncher.rb +8 -1
  42. data/app/services/hr_lite/calculators/esi.rb +7 -4
  43. data/app/services/hr_lite/calculators/professional_tax.rb +5 -1
  44. data/app/services/hr_lite/calculators/tds.rb +8 -1
  45. data/app/services/hr_lite/leave_day_counter.rb +9 -4
  46. data/app/services/hr_lite/overview_query.rb +12 -1
  47. data/app/services/hr_lite/payroll_run_processor.rb +5 -0
  48. data/app/services/hr_lite/slip_builder.rb +54 -8
  49. data/app/views/hr_lite/admin/attendances/index.html.erb +4 -1
  50. data/app/views/hr_lite/admin/comp_off_requests/show.html.erb +19 -5
  51. data/app/views/hr_lite/admin/employees/_form.html.erb +30 -3
  52. data/app/views/hr_lite/admin/employees/index.html.erb +3 -0
  53. data/app/views/hr_lite/admin/employees/show.html.erb +34 -18
  54. data/app/views/hr_lite/admin/leave_requests/show.html.erb +37 -5
  55. data/app/views/hr_lite/admin/overview/index.html.erb +21 -0
  56. data/app/views/hr_lite/admin/payroll_runs/show.html.erb +3 -1
  57. data/app/views/hr_lite/admin/regularization_requests/show.html.erb +18 -4
  58. data/app/views/hr_lite/admin/settings/edit.html.erb +7 -0
  59. data/app/views/hr_lite/attendance/_punch_card.html.erb +22 -2
  60. data/app/views/hr_lite/career/show.html.erb +7 -1
  61. data/app/views/hr_lite/salary_slips/_slip_detail.html.erb +5 -1
  62. data/app/views/hr_lite/salary_slips/pdf.html.erb +5 -5
  63. data/app/views/layouts/hr_lite/application.html.erb +47 -4
  64. data/config/routes.rb +1 -1
  65. data/db/migrate/20260720094454_add_employee_code_prefix_to_hr_lite_settings.rb +7 -0
  66. data/db/migrate/20260807184103_add_live_uniqueness_indexes_to_hr_lite.rb +28 -0
  67. data/db/migrate/20260807184426_add_fy_opening_to_hr_lite_employee_profiles.rb +14 -0
  68. data/db/migrate/20260807184939_add_out_of_window_days_to_hr_lite_salary_slips.rb +12 -0
  69. data/lib/hr_lite/amount_in_words.rb +15 -2
  70. data/lib/hr_lite/configuration.rb +8 -0
  71. data/lib/hr_lite/money.rb +18 -0
  72. data/lib/hr_lite/notifications.rb +23 -8
  73. data/lib/hr_lite/statutory_rate_card.rb +6 -1
  74. data/lib/hr_lite/version.rb +1 -1
  75. data/lib/hr_lite.rb +9 -1
  76. metadata +7 -1
data/lib/hr_lite/money.rb CHANGED
@@ -29,5 +29,23 @@ module HrLite
29
29
  def round_to_10(value)
30
30
  (d(value) / 10).round(0, BigDecimal::ROUND_HALF_UP) * 10
31
31
  end
32
+
33
+ # Indian digit grouping: 12,34,567 — not the western 1,234,567. It lives
34
+ # here rather than only in the view helper because the slip PDF is
35
+ # rendered by HOST code (config.render_pdf), where engine helpers are out
36
+ # of scope — which is why the PDF printed a bare "75000.0" next to the
37
+ # web slip's "₹75,000.00".
38
+ def format(amount)
39
+ return "—" if amount.nil?
40
+
41
+ whole, fraction = round2(amount).to_s("F").split(".")
42
+ sign = whole.start_with?("-") ? "-" : ""
43
+ whole = whole.delete_prefix("-")
44
+ if whole.length > 3
45
+ head = whole[0..-4].reverse.scan(/\d{1,2}/).join(",").reverse
46
+ whole = "#{head},#{whole[-3..]}"
47
+ end
48
+ "#{sign}#{HrLite.config.currency_symbol}#{whole}.#{fraction.ljust(2, '0')}"
49
+ end
32
50
  end
33
51
  end
@@ -49,17 +49,27 @@ module HrLite
49
49
  DEFAULT_MATRIX.merge(HrLite.config.notification_matrix || {})
50
50
  end
51
51
 
52
- def publish(event, title:, body: nil, path: nil, bell_to: [], email_to: [], lines: [], diff: nil, link_url: nil)
52
+ # `leadership:` overrides title/body/path for the leadership copies of an
53
+ # event. Some events are written in the second person and point at an
54
+ # employee-scoped path ("Your appraisal has been shared", /appraisals/7):
55
+ # fanned out verbatim, leadership read them as being about themselves and
56
+ # followed a link their own scope 404s or resolves to their own record.
57
+ def publish(event, title:, body: nil, path: nil, bell_to: [], email_to: [], lines: [],
58
+ diff: nil, link_url: nil, leadership: {})
53
59
  row = matrix[event.to_s]
54
60
  unless row
55
61
  Rails.logger.warn("[hr_lite] unknown notification event #{event}")
56
62
  return
57
63
  end
58
64
 
65
+ lead_title = leadership[:title] || title
66
+ lead_body = leadership.key?(:body) ? leadership[:body] : body
67
+ lead_path = leadership[:path] || path
68
+
59
69
  deliver_bells(event, row, bell_to, title, body, path)
60
70
  deliver_emails(row, email_to, title, body, path, lines, link_url)
61
- deliver_leadership_email(event, row, title, body, path, lines, diff)
62
- deliver_leadership_bells(event, row, bell_to, title, body, path)
71
+ deliver_leadership_email(event, row, lead_title, lead_body, lead_path, lines, diff)
72
+ deliver_leadership_bells(event, row, bell_to, lead_title, lead_body, lead_path)
63
73
  nil
64
74
  end
65
75
 
@@ -76,15 +86,20 @@ module HrLite
76
86
  def deliver_emails(row, email_to, title, body, path, lines, link_url = nil)
77
87
  return unless row[:email]
78
88
 
89
+ # Rescued PER RECIPIENT. Wrapping the whole loop meant one bad address
90
+ # or a single enqueue failure silently dropped everyone after it — on
91
+ # a team-wide leave notice, most of the team.
79
92
  Array(email_to).compact.uniq.each do |user|
80
93
  next if user.email.blank?
81
94
 
82
- EventMailer.event(to: user.email, subject: title, heading: title,
83
- body: body, lines: lines, path: path,
84
- link_url: link_url).deliver_later
95
+ begin
96
+ EventMailer.event(to: user.email, subject: title, heading: title,
97
+ body: body, lines: lines, path: path,
98
+ link_url: link_url).deliver_later
99
+ rescue => e
100
+ Rails.logger.error("[hr_lite] event email failed: #{e.class}: #{e.message}")
101
+ end
85
102
  end
86
- rescue => e
87
- Rails.logger.error("[hr_lite] event email failed: #{e.class}: #{e.message}")
88
103
  end
89
104
 
90
105
  def deliver_leadership_email(event, row, title, body, path, lines, diff)
@@ -24,11 +24,16 @@ module HrLite
24
24
  "none" => [],
25
25
  "uttar_pradesh" => [],
26
26
  "uttarakhand" => [],
27
- "karnataka" => [ { above: r("24999"), monthly: r("200") } ]
27
+ # Inclusive lower bounds. `above: 24999` with a strict `>` taxed a
28
+ # prorated gross of ₹24,999.50, which is below the real threshold.
29
+ "karnataka" => [ { from: r("25000"), monthly: r("200") } ]
28
30
  },
29
31
  income_tax: {
30
32
  "new" => {
31
33
  standard_deduction: r("75000"), rebate_cap: r("1200000"), cess_rate: r("0.04"),
34
+ # §115BAC carries marginal relief just above the rebate cap; the
35
+ # old regime does not, so this is a per-regime flag.
36
+ marginal_relief: true,
32
37
  slabs: [
33
38
  [ r("0"), r("400000"), r("0") ],
34
39
  [ r("400000"), r("800000"), r("0.05") ],
@@ -1,3 +1,3 @@
1
1
  module HrLite
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.2"
3
3
  end
data/lib/hr_lite.rb CHANGED
@@ -38,6 +38,10 @@ module HrLite
38
38
  user.present? && !!config.leadership_check.call(user)
39
39
  end
40
40
 
41
+ def superadmin?(user)
42
+ user.present? && !!config.superadmin_check.call(user)
43
+ end
44
+
41
45
  # Leadership members resolvable to actual user records (for bell
42
46
  # notifications). Emails configured but absent from the user table are
43
47
  # still reachable by email — see Notifications.
@@ -48,8 +52,12 @@ module HrLite
48
52
  user_klass.where("LOWER(#{user_klass.table_name}.email) IN (?)", emails)
49
53
  end
50
54
 
55
+ # Every domain event that bells the admins calls this. Loading and
56
+ # instantiating the entire users table to run admin_check per row is fine
57
+ # at ten staff and silly at a thousand — so it starts from employees_scope
58
+ # (which a host narrows to real staff) rather than every row that exists.
51
59
  def admin_users
52
- user_klass.all.select { |u| admin?(u) }
60
+ employees.select { |u| admin?(u) }
53
61
  end
54
62
 
55
63
  # Everyone HR tracks (host-overridable to exclude bots/test accounts),
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hr_lite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - kshitiz sinha
@@ -50,6 +50,7 @@ files:
50
50
  - MIT-LICENSE
51
51
  - README.md
52
52
  - Rakefile
53
+ - app/assets/javascripts/hr_lite/confirm.js
53
54
  - app/assets/javascripts/hr_lite/geo_punch.js
54
55
  - app/assets/javascripts/hr_lite/mention.js
55
56
  - app/assets/stylesheets/hr_lite/application.css
@@ -74,6 +75,7 @@ files:
74
75
  - app/controllers/hr_lite/admin/salary_slips_controller.rb
75
76
  - app/controllers/hr_lite/admin/salary_structures_controller.rb
76
77
  - app/controllers/hr_lite/admin/settings_controller.rb
78
+ - app/controllers/hr_lite/admin/superadmin_controller.rb
77
79
  - app/controllers/hr_lite/application_controller.rb
78
80
  - app/controllers/hr_lite/appraisals_controller.rb
79
81
  - app/controllers/hr_lite/attendance_controller.rb
@@ -236,6 +238,10 @@ files:
236
238
  - db/migrate/20260719104316_create_hr_lite_comp_off_requests.rb
237
239
  - db/migrate/20260719104317_create_hr_lite_regularization_requests.rb
238
240
  - db/migrate/20260719162154_add_manager_to_hr_lite_employee_profiles.rb
241
+ - db/migrate/20260720094454_add_employee_code_prefix_to_hr_lite_settings.rb
242
+ - db/migrate/20260807184103_add_live_uniqueness_indexes_to_hr_lite.rb
243
+ - db/migrate/20260807184426_add_fy_opening_to_hr_lite_employee_profiles.rb
244
+ - db/migrate/20260807184939_add_out_of_window_days_to_hr_lite_salary_slips.rb
239
245
  - lib/generators/hr_lite/install/install_generator.rb
240
246
  - lib/generators/hr_lite/install/templates/AFTER_INSTALL
241
247
  - lib/generators/hr_lite/install/templates/initializer.rb