hr_lite 0.23.1 → 0.26.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -0
  3. data/README.md +4 -1
  4. data/app/assets/javascripts/hr_lite/tax_preview.js +38 -0
  5. data/app/assets/stylesheets/hr_lite/hr_lite.css +12 -1
  6. data/app/controllers/hr_lite/admin/ca_reviews_controller.rb +76 -0
  7. data/app/controllers/hr_lite/admin/payroll_runs_controller.rb +14 -4
  8. data/app/controllers/hr_lite/admin/salary_slips_controller.rb +4 -0
  9. data/app/controllers/hr_lite/admin/salary_structures_controller.rb +6 -16
  10. data/app/controllers/hr_lite/admin/tax_declarations_controller.rb +46 -0
  11. data/app/controllers/hr_lite/tax_declarations_controller.rb +19 -1
  12. data/app/controllers/hr_lite/timelines_controller.rb +2 -1
  13. data/app/helpers/hr_lite/application_helper.rb +7 -0
  14. data/app/jobs/hr_lite/ca_review_overdue_job.rb +19 -0
  15. data/app/jobs/hr_lite/ca_review_reminder_job.rb +21 -0
  16. data/app/jobs/hr_lite/payroll_auto_approve_job.rb +21 -0
  17. data/app/jobs/hr_lite/payroll_auto_draft_job.rb +1 -1
  18. data/app/models/hr_lite/audit_log.rb +1 -1
  19. data/app/models/hr_lite/ca_review.rb +66 -0
  20. data/app/models/hr_lite/payroll_run.rb +47 -3
  21. data/app/models/hr_lite/role.rb +3 -0
  22. data/app/models/hr_lite/salary_structure.rb +22 -8
  23. data/app/models/hr_lite/statutory_rate_card_record.rb +6 -6
  24. data/app/models/hr_lite/tax_declaration.rb +24 -0
  25. data/app/models/hr_lite/tax_declaration_item.rb +56 -4
  26. data/app/services/hr_lite/calculators/tds.rb +7 -6
  27. data/app/services/hr_lite/payroll_run_processor.rb +9 -0
  28. data/app/services/hr_lite/slip_builder.rb +1 -1
  29. data/app/services/hr_lite/tax_computation.rb +61 -0
  30. data/app/services/hr_lite/timeline.rb +10 -1
  31. data/app/views/hr_lite/admin/ca_reviews/index.html.erb +94 -0
  32. data/app/views/hr_lite/admin/employees/show.html.erb +3 -1
  33. data/app/views/hr_lite/admin/payroll_runs/show.html.erb +17 -7
  34. data/app/views/hr_lite/admin/salary_structures/_form.html.erb +15 -7
  35. data/app/views/hr_lite/admin/statutory_rate_cards/_form.html.erb +1 -1
  36. data/app/views/hr_lite/admin/tax_declarations/index.html.erb +1 -0
  37. data/app/views/hr_lite/admin/tax_declarations/overview.html.erb +39 -0
  38. data/app/views/hr_lite/admin/tax_declarations/person.html.erb +10 -0
  39. data/app/views/hr_lite/admin/tax_declarations/show.html.erb +19 -2
  40. data/app/views/hr_lite/leave_requests/_balance_chips.html.erb +6 -4
  41. data/app/views/hr_lite/tax/_computation.html.erb +56 -0
  42. data/app/views/hr_lite/tax_declarations/show.html.erb +61 -59
  43. data/app/views/hr_lite/timelines/show.html.erb +1 -0
  44. data/config/routes.rb +8 -2
  45. data/db/migrate/20261010100000_add_ctc_to_hr_lite_salary_structures.rb +9 -0
  46. data/db/migrate/20261010100100_add_approval_to_hr_lite_payroll_runs.rb +10 -0
  47. data/db/migrate/20261010100200_grant_hr_lite_payroll_approve.rb +22 -0
  48. data/db/migrate/20261010100300_seed_hr_lite_fy2026_rate_cards.rb +16 -0
  49. data/db/migrate/20261010100400_add_tax_fields_to_hr_lite_tax_declaration_items.rb +9 -0
  50. data/db/migrate/20261010100500_grant_hr_lite_tax_to_hr.rb +12 -0
  51. data/db/migrate/20261010110000_create_hr_lite_ca_reviews.rb +21 -0
  52. data/db/migrate/20261010110100_seed_hr_lite_ca_role.rb +14 -0
  53. data/lib/hr_lite/notifications.rb +5 -0
  54. data/lib/hr_lite/permissions.rb +2 -0
  55. data/lib/hr_lite/role_seeds.rb +9 -3
  56. data/lib/hr_lite/statutory_rate_card.rb +65 -43
  57. data/lib/hr_lite/statutory_seeds.rb +4 -2
  58. data/lib/hr_lite/version.rb +1 -1
  59. data/lib/hr_lite.rb +9 -1
  60. metadata +20 -1
@@ -18,50 +18,72 @@ module HrLite
18
18
  def self.r(value) = BigDecimal(value.to_s)
19
19
  private_class_method :r
20
20
 
21
+ FY2025_TAX = {
22
+ "new" => {
23
+ standard_deduction: r("75000"), rebate_cap: r("1200000"), cess_rate: r("0.04"),
24
+ # §115BAC carries marginal relief just above the rebate cap; the
25
+ # old regime does not, so this is a per-regime flag.
26
+ marginal_relief: true,
27
+ slabs: [
28
+ [ r("0"), r("400000"), r("0") ],
29
+ [ r("400000"), r("800000"), r("0.05") ],
30
+ [ r("800000"), r("1200000"), r("0.10") ],
31
+ [ r("1200000"), r("1600000"), r("0.15") ],
32
+ [ r("1600000"), r("2000000"), r("0.20") ],
33
+ [ r("2000000"), r("2400000"), r("0.25") ],
34
+ [ r("2400000"), nil, r("0.30") ]
35
+ ]
36
+ },
37
+ "old" => {
38
+ standard_deduction: r("50000"), rebate_cap: r("500000"), cess_rate: r("0.04"),
39
+ slabs: [
40
+ [ r("0"), r("250000"), r("0") ],
41
+ [ r("250000"), r("500000"), r("0.05") ],
42
+ [ r("500000"), r("1000000"), r("0.20") ],
43
+ [ r("1000000"), nil, r("0.30") ]
44
+ ]
45
+ }
46
+ }.freeze
47
+ private_constant :FY2025_TAX
48
+
49
+ PF_15K = {
50
+ employee_rate: r("0.12"), employer_rate: r("0.12"), eps_rate: r("0.0833"),
51
+ wage_ceiling: r("15000"), eps_wage_ceiling: r("15000"),
52
+ edli_rate: r("0.005"), edli_ceiling: r("15000"), admin_rate: r("0.005")
53
+ }.freeze
54
+ private_constant :PF_15K
55
+
56
+ ESI = { employee_rate: r("0.0075"), employer_rate: r("0.0325"), gross_ceiling: r("21000") }.freeze
57
+ private_constant :ESI
58
+
59
+ PT = {
60
+ # Neither UP nor Uttarakhand levies professional tax today; both
61
+ # ship empty (PT = 0). Karnataka included as a worked template.
62
+ "none" => [],
63
+ "uttar_pradesh" => [],
64
+ "uttarakhand" => [],
65
+ # Inclusive lower bounds. `above: 24999` with a strict `>` taxed a
66
+ # prorated gross of ₹24,999.50, which is below the real threshold.
67
+ "karnataka" => [ { from: r("25000"), monthly: r("200") } ]
68
+ }.freeze
69
+ private_constant :PT
70
+
71
+ # Cards the gem ships already verified, with where the figures come from.
72
+ # StatutorySeeds stamps the source as the verifier, so nobody has to.
73
+ SOURCES = {
74
+ Date.new(2026, 4, 1) => "Finance Act 2026: slabs, standard deduction and §87A rebate unchanged from " \
75
+ "FY 2025-26 (Income-tax Act 2025 in force from 1 April 2026; salary TDS §392). " \
76
+ "PF ₹15,000 and ESI ₹21,000 ceilings unchanged.",
77
+ Date.new(2026, 10, 1) => "EPF/EPS/EDLI wage ceiling ₹25,000 — Ministry of Labour Notification S.O. 5109(E), " \
78
+ "17 Sep 2026; applied from October 2026 payroll. Tax slabs and ESI as FY 2026-27."
79
+ }.freeze
80
+
21
81
  CARDS = {
22
- Date.new(2025, 4, 1) => {
23
- pf: {
24
- employee_rate: r("0.12"), employer_rate: r("0.12"), eps_rate: r("0.0833"),
25
- wage_ceiling: r("15000"), eps_wage_ceiling: r("15000"),
26
- edli_rate: r("0.005"), edli_ceiling: r("15000"), admin_rate: r("0.005")
27
- },
28
- esi: { employee_rate: r("0.0075"), employer_rate: r("0.0325"), gross_ceiling: r("21000") },
29
- pt: {
30
- # Neither UP nor Uttarakhand levies professional tax today; both
31
- # ship empty (PT = 0). Karnataka included as a worked template.
32
- "none" => [],
33
- "uttar_pradesh" => [],
34
- "uttarakhand" => [],
35
- # Inclusive lower bounds. `above: 24999` with a strict `>` taxed a
36
- # prorated gross of ₹24,999.50, which is below the real threshold.
37
- "karnataka" => [ { from: r("25000"), monthly: r("200") } ]
38
- },
39
- income_tax: {
40
- "new" => {
41
- standard_deduction: r("75000"), rebate_cap: r("1200000"), cess_rate: r("0.04"),
42
- # §115BAC carries marginal relief just above the rebate cap; the
43
- # old regime does not, so this is a per-regime flag.
44
- marginal_relief: true,
45
- slabs: [
46
- [ r("0"), r("400000"), r("0") ],
47
- [ r("400000"), r("800000"), r("0.05") ],
48
- [ r("800000"), r("1200000"), r("0.10") ],
49
- [ r("1200000"), r("1600000"), r("0.15") ],
50
- [ r("1600000"), r("2000000"), r("0.20") ],
51
- [ r("2000000"), r("2400000"), r("0.25") ],
52
- [ r("2400000"), nil, r("0.30") ]
53
- ]
54
- },
55
- "old" => {
56
- standard_deduction: r("50000"), rebate_cap: r("500000"), cess_rate: r("0.04"),
57
- slabs: [
58
- [ r("0"), r("250000"), r("0") ],
59
- [ r("250000"), r("500000"), r("0.05") ],
60
- [ r("500000"), r("1000000"), r("0.20") ],
61
- [ r("1000000"), nil, r("0.30") ]
62
- ]
63
- }
64
- }
82
+ Date.new(2025, 4, 1) => { pf: PF_15K, esi: ESI, pt: PT, income_tax: FY2025_TAX },
83
+ Date.new(2026, 4, 1) => { pf: PF_15K, esi: ESI, pt: PT, income_tax: FY2025_TAX },
84
+ Date.new(2026, 10, 1) => {
85
+ pf: PF_15K.merge(wage_ceiling: r("25000"), eps_wage_ceiling: r("25000"), edli_ceiling: r("25000")),
86
+ esi: ESI, pt: PT, income_tax: FY2025_TAX
65
87
  }
66
88
  }.freeze
67
89
 
@@ -20,8 +20,10 @@ module HrLite
20
20
  pf: stringify(card[:pf]),
21
21
  esi: stringify(card[:esi]),
22
22
  income_tax: card[:income_tax].transform_values { |regime| stringify_regime(regime) },
23
- notes: "Shipped with hr_lite #{HrLite::VERSION}. Confirm with your " \
24
- "accountant, then record who verified it."
23
+ notes: StatutoryRateCard::SOURCES[effective_from] ||
24
+ "Shipped with hr_lite #{HrLite::VERSION}. Confirm with your accountant, then record who verified it.",
25
+ verified_by: (StatutoryRateCard::SOURCES.key?(effective_from) ? "hr_lite #{HrLite::VERSION} (official source in the notes)" : nil),
26
+ verified_on: (Date.current if StatutoryRateCard::SOURCES.key?(effective_from))
25
27
  )
26
28
  "rate card FY #{FinancialYear.label(effective_from)}"
27
29
  end
@@ -1,3 +1,3 @@
1
1
  module HrLite
2
- VERSION = "0.23.1"
2
+ VERSION = "0.26.0"
3
3
  end
data/lib/hr_lite.rb CHANGED
@@ -114,7 +114,15 @@ module HrLite
114
114
  # Everyone HR tracks (host-overridable to exclude bots/test accounts),
115
115
  # sorted by display name for team screens.
116
116
  def employees
117
- config.employees_scope.call.sort_by { |u| display_name(u).downcase }
117
+ outside = outside_accountant_ids
118
+ config.employees_scope.call.reject { |u| outside.include?(u.id) }.sort_by { |u| display_name(u).downcase }
119
+ end
120
+
121
+ # CA logins with no HR profile: they review pay, they are not staff, so
122
+ # no attendance, leave, payroll or team list ever counts them.
123
+ def outside_accountant_ids
124
+ ids = HrLite::RoleAssignment.joins(:role).where(hr_lite_roles: { name: HrLite::Role::CA }).pluck(:user_id)
125
+ (ids - HrLite::EmployeeProfile.where(user_id: ids).pluck(:user_id)).to_set
118
126
  end
119
127
 
120
128
  # employees minus anyone whose profile says they have exited — user
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.23.1
4
+ version: 0.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kshitiz sinha
@@ -70,6 +70,7 @@ files:
70
70
  - app/assets/javascripts/hr_lite/nav.js
71
71
  - app/assets/javascripts/hr_lite/print.js
72
72
  - app/assets/javascripts/hr_lite/select_filter.js
73
+ - app/assets/javascripts/hr_lite/tax_preview.js
73
74
  - app/assets/stylesheets/hr_lite/application.css
74
75
  - app/assets/stylesheets/hr_lite/hr_lite.css
75
76
  - app/controllers/hr_lite/admin/appraisals_controller.rb
@@ -79,6 +80,7 @@ files:
79
80
  - app/controllers/hr_lite/admin/awards_controller.rb
80
81
  - app/controllers/hr_lite/admin/base_controller.rb
81
82
  - app/controllers/hr_lite/admin/benefits_controller.rb
83
+ - app/controllers/hr_lite/admin/ca_reviews_controller.rb
82
84
  - app/controllers/hr_lite/admin/checklists_controller.rb
83
85
  - app/controllers/hr_lite/admin/comp_off_requests_controller.rb
84
86
  - app/controllers/hr_lite/admin/designation_changes_controller.rb
@@ -142,9 +144,12 @@ files:
142
144
  - app/jobs/hr_lite/application_job.rb
143
145
  - app/jobs/hr_lite/approval_escalation_job.rb
144
146
  - app/jobs/hr_lite/attendance_close_job.rb
147
+ - app/jobs/hr_lite/ca_review_overdue_job.rb
148
+ - app/jobs/hr_lite/ca_review_reminder_job.rb
145
149
  - app/jobs/hr_lite/daily_digest_job.rb
146
150
  - app/jobs/hr_lite/document_expiry_job.rb
147
151
  - app/jobs/hr_lite/leave_year_rollover_job.rb
152
+ - app/jobs/hr_lite/payroll_auto_approve_job.rb
148
153
  - app/jobs/hr_lite/payroll_auto_draft_job.rb
149
154
  - app/jobs/hr_lite/slips_ready_job.rb
150
155
  - app/mailers/hr_lite/application_mailer.rb
@@ -165,6 +170,7 @@ files:
165
170
  - app/models/hr_lite/award.rb
166
171
  - app/models/hr_lite/benefit.rb
167
172
  - app/models/hr_lite/benefit_enrolment.rb
173
+ - app/models/hr_lite/ca_review.rb
168
174
  - app/models/hr_lite/checklist_item.rb
169
175
  - app/models/hr_lite/checklist_template.rb
170
176
  - app/models/hr_lite/comp_off_request.rb
@@ -216,6 +222,7 @@ files:
216
222
  - app/services/hr_lite/payroll_run_processor.rb
217
223
  - app/services/hr_lite/pdf_renderer.rb
218
224
  - app/services/hr_lite/slip_builder.rb
225
+ - app/services/hr_lite/tax_computation.rb
219
226
  - app/services/hr_lite/team_day.rb
220
227
  - app/services/hr_lite/timeline.rb
221
228
  - app/services/hr_lite/working_calendar.rb
@@ -234,6 +241,7 @@ files:
234
241
  - app/views/hr_lite/admin/awards/new.html.erb
235
242
  - app/views/hr_lite/admin/benefits/index.html.erb
236
243
  - app/views/hr_lite/admin/benefits/new.html.erb
244
+ - app/views/hr_lite/admin/ca_reviews/index.html.erb
237
245
  - app/views/hr_lite/admin/checklists/index.html.erb
238
246
  - app/views/hr_lite/admin/comp_off_requests/index.html.erb
239
247
  - app/views/hr_lite/admin/comp_off_requests/show.html.erb
@@ -299,6 +307,8 @@ files:
299
307
  - app/views/hr_lite/admin/statutory_rate_cards/index.html.erb
300
308
  - app/views/hr_lite/admin/statutory_rate_cards/new.html.erb
301
309
  - app/views/hr_lite/admin/tax_declarations/index.html.erb
310
+ - app/views/hr_lite/admin/tax_declarations/overview.html.erb
311
+ - app/views/hr_lite/admin/tax_declarations/person.html.erb
302
312
  - app/views/hr_lite/admin/tax_declarations/show.html.erb
303
313
  - app/views/hr_lite/appraisals/index.html.erb
304
314
  - app/views/hr_lite/appraisals/show.html.erb
@@ -349,6 +359,7 @@ files:
349
359
  - app/views/hr_lite/shared/_bell.html.erb
350
360
  - app/views/hr_lite/shared/_form_errors.html.erb
351
361
  - app/views/hr_lite/shared/_pagination.html.erb
362
+ - app/views/hr_lite/tax/_computation.html.erb
352
363
  - app/views/hr_lite/tax_declarations/show.html.erb
353
364
  - app/views/hr_lite/team/show.html.erb
354
365
  - app/views/hr_lite/timelines/_events.html.erb
@@ -400,6 +411,14 @@ files:
400
411
  - db/migrate/20261006120100_add_half_day_part_to_hr_lite_attendance_records.rb
401
412
  - db/migrate/20261010090000_create_hr_lite_performance_plans.rb
402
413
  - db/migrate/20261010090100_create_hr_lite_awards.rb
414
+ - db/migrate/20261010100000_add_ctc_to_hr_lite_salary_structures.rb
415
+ - db/migrate/20261010100100_add_approval_to_hr_lite_payroll_runs.rb
416
+ - db/migrate/20261010100200_grant_hr_lite_payroll_approve.rb
417
+ - db/migrate/20261010100300_seed_hr_lite_fy2026_rate_cards.rb
418
+ - db/migrate/20261010100400_add_tax_fields_to_hr_lite_tax_declaration_items.rb
419
+ - db/migrate/20261010100500_grant_hr_lite_tax_to_hr.rb
420
+ - db/migrate/20261010110000_create_hr_lite_ca_reviews.rb
421
+ - db/migrate/20261010110100_seed_hr_lite_ca_role.rb
403
422
  - lib/generators/hr_lite/install/install_generator.rb
404
423
  - lib/generators/hr_lite/install/templates/AFTER_INSTALL
405
424
  - lib/generators/hr_lite/install/templates/initializer.rb