hr_lite 0.20.0 → 0.20.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/app/assets/javascripts/hr_lite/geo_punch.js +10 -2
- data/app/controllers/hr_lite/admin/attendances_controller.rb +2 -1
- data/app/controllers/hr_lite/admin/comp_off_requests_controller.rb +2 -1
- data/app/controllers/hr_lite/admin/documents_controller.rb +2 -0
- data/app/controllers/hr_lite/admin/expenses_controller.rb +2 -1
- data/app/controllers/hr_lite/admin/leave_balances_controller.rb +1 -0
- data/app/controllers/hr_lite/admin/leave_requests_controller.rb +3 -2
- data/app/controllers/hr_lite/admin/overview_controller.rb +1 -1
- data/app/controllers/hr_lite/admin/payroll_runs_controller.rb +5 -1
- data/app/controllers/hr_lite/admin/regularization_requests_controller.rb +2 -1
- data/app/controllers/hr_lite/admin/reports_controller.rb +3 -1
- data/app/controllers/hr_lite/admin/tax_declarations_controller.rb +1 -0
- data/app/models/hr_lite/audit_log.rb +1 -1
- data/app/models/hr_lite/document.rb +3 -0
- data/app/models/hr_lite/expense.rb +8 -0
- data/app/models/hr_lite/leave_request.rb +3 -3
- data/app/services/hr_lite/overview_query.rb +13 -7
- data/app/services/hr_lite/slip_builder.rb +2 -1
- data/app/views/hr_lite/admin/attendances/index.html.erb +1 -1
- data/lib/hr_lite/notifications.rb +1 -1
- data/lib/hr_lite/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 36aafaef8b40a06e92550352b9b1b316d793185ffa871c2a872430c44a88d23c
|
|
4
|
+
data.tar.gz: 957a8842a0ce1fa20e200c9cd0795a9c75eeff204be0d39aa9198781fcfc13ed
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e7eeeb898e3798322fdf75539115586a9513b8c048edd51b89af45bba9cc37d7577e4645cf1b3c6707158b1c51d49195d2d803dddcab5dae2990b6607a6a8772
|
|
7
|
+
data.tar.gz: 74eb90b519e1ca6f7ece2261bbf02f4e0cc094ebbd6faf054eb1945af71a4d4c950551ce7d10c353b14e652bbe2af3ce9978d7af4a729882f3e87214ba76549d
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.20.2] - 2026-10-05
|
|
11
|
+
|
|
12
|
+
No migration.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **Leave team notice goes out on apply, by email too.** The whole team
|
|
17
|
+
(minus the applicant) gets the bell and an email the moment leave is
|
|
18
|
+
applied, not after approval. The reason is still left out. Hosts can
|
|
19
|
+
mute either channel with the `leave.team_notice` matrix row.
|
|
20
|
+
- **A blocked location holds the punch.** Browsers ask for location once;
|
|
21
|
+
after a "Don't allow" every later punch was filed without GPS and
|
|
22
|
+
flagged. The punch now waits and tells the employee to allow location,
|
|
23
|
+
then tap again. Timeout and no-GPS devices still punch (and flag).
|
|
24
|
+
- Team attendance board: "Fix / month" is now "Edit / half day", the
|
|
25
|
+
screen where HR changes a marked day to a half day.
|
|
26
|
+
|
|
27
|
+
## [0.20.1] - 2026-10-03
|
|
28
|
+
|
|
29
|
+
Security fixes. No migration.
|
|
30
|
+
|
|
31
|
+
### Upgrading
|
|
32
|
+
|
|
33
|
+
Document numbers are now encrypted with Active Record Encryption. Rows
|
|
34
|
+
saved before this release are plain text, so the host needs
|
|
35
|
+
`config.active_record.encryption.support_unencrypted_data = true` to keep
|
|
36
|
+
reading them, and should re-save each `HrLite::Document` once to encrypt
|
|
37
|
+
the old numbers.
|
|
38
|
+
|
|
39
|
+
### Security
|
|
40
|
+
|
|
41
|
+
- **Nobody decides their own request.** A manager could approve their own
|
|
42
|
+
leave, comp-off and attendance fixes, Finance its own expense claims, and
|
|
43
|
+
anyone recording leave for someone else could record their own as
|
|
44
|
+
approved. HR could verify or reject its own documents. All refused now.
|
|
45
|
+
- **Nobody edits their own records from the admin screens.** HR rewriting
|
|
46
|
+
its own attendance, adjusting its own leave balance, or Finance verifying
|
|
47
|
+
its own tax declaration is refused.
|
|
48
|
+
- **Document numbers are encrypted.** Aadhaar, PAN and passport numbers
|
|
49
|
+
were stored in plain text and copied into audit diffs and the leadership
|
|
50
|
+
audit email. Loans join the money tier for audit mail.
|
|
51
|
+
- **A rejected tax declaration no longer lowers TDS.**
|
|
52
|
+
- **Payroll CSVs defuse formulas.** Register and report cells that open
|
|
53
|
+
with `=`, `+`, `-` or `@` are escaped, since names are staff-typed.
|
|
54
|
+
- **The payout register needs `payroll.export`**, not just
|
|
55
|
+
`payroll.manage`.
|
|
56
|
+
- **The admin overview respects reach.** A team-scoped manager sees only
|
|
57
|
+
the people they reach, not the whole company.
|
|
58
|
+
- **Expense receipts** take the same type and size rule as documents.
|
|
59
|
+
|
|
10
60
|
## [0.20.0] - 2026-10-01
|
|
11
61
|
|
|
12
62
|
Salary as CTC, and who is out today. No migration.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// <button type="submit">…</button>
|
|
7
7
|
// <span data-hrl-geo-status hidden></span>
|
|
8
8
|
// </form>
|
|
9
|
-
//
|
|
9
|
+
// A denied permission holds the punch; timeout/unavailable submit anyway
|
|
10
10
|
// with geo_status set; the server flags them.
|
|
11
11
|
(function () {
|
|
12
12
|
"use strict";
|
|
@@ -45,7 +45,15 @@
|
|
|
45
45
|
send("ok");
|
|
46
46
|
},
|
|
47
47
|
function (err) {
|
|
48
|
-
|
|
48
|
+
// Browsers ask only once; a past "Don't allow" denies silently forever.
|
|
49
|
+
// Hold the punch so it is never filed (and flagged) without GPS.
|
|
50
|
+
if (err && err.code === 1) {
|
|
51
|
+
locating = false;
|
|
52
|
+
if (button) { button.disabled = false; button.removeAttribute("aria-busy"); }
|
|
53
|
+
if (label) label.textContent = "Location is blocked. Allow location for this site in your browser settings, then tap again.";
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
send("timeout");
|
|
49
57
|
},
|
|
50
58
|
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 }
|
|
51
59
|
);
|
|
@@ -82,7 +82,8 @@ module HrLite
|
|
|
82
82
|
# authorities, so they resolve through different permissions — a role
|
|
83
83
|
# can be given the board without being given the pencil.
|
|
84
84
|
def find_visible_employee = employee_within("attendance.view")
|
|
85
|
-
|
|
85
|
+
# Nobody rewrites their own punches: team and company scope include the holder.
|
|
86
|
+
def find_manageable_employee = employee_within("attendance.manage").tap { |e| raise ActiveRecord::RecordNotFound if e.id == hr_current_user.id }
|
|
86
87
|
|
|
87
88
|
def employee_within(permission)
|
|
88
89
|
employee = HrLite.user_klass.find(params[:user_id])
|
|
@@ -40,8 +40,9 @@ module HrLite
|
|
|
40
40
|
# A manager decides for their own reports; HR decides for everyone.
|
|
41
41
|
# Scoped through the relation, so reaching for somebody else's report
|
|
42
42
|
# is a 404 rather than a 403 that confirms the person exists.
|
|
43
|
+
# Nobody decides their own request: team scope includes the approver.
|
|
43
44
|
def decidable
|
|
44
|
-
hr_scope(CompOffRequest.all, "leave.approve")
|
|
45
|
+
hr_scope(CompOffRequest.all, "leave.approve").where.not(user_id: hr_current_user.id)
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
def find_decidable = decidable.find(params[:id])
|
|
@@ -54,6 +54,8 @@ module HrLite
|
|
|
54
54
|
# whoever happens to run HR.
|
|
55
55
|
def find_manageable
|
|
56
56
|
document = Document.find(params[:id])
|
|
57
|
+
# readable_by? always admits the owner, and nobody verifies their own paperwork.
|
|
58
|
+
raise ActiveRecord::RecordNotFound if document.user_id == hr_current_user.id
|
|
57
59
|
raise ActiveRecord::RecordNotFound unless document.readable_by?(hr_current_user)
|
|
58
60
|
|
|
59
61
|
document
|
|
@@ -39,7 +39,8 @@ module HrLite
|
|
|
39
39
|
|
|
40
40
|
def require_deciding! = hr_require_permission!("expense.approve", scope: :team)
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
# Nobody approves their own claim: team scope includes the approver.
|
|
43
|
+
def decidable = hr_scope(Expense.all, "expense.approve").where.not(user_id: hr_current_user.id)
|
|
43
44
|
end
|
|
44
45
|
end
|
|
45
46
|
end
|
|
@@ -13,6 +13,7 @@ module HrLite
|
|
|
13
13
|
# A manager reaches this screen through leave.approve; changing
|
|
14
14
|
# somebody's quota is leave.manage, for THAT person.
|
|
15
15
|
hr_require_reach!("leave.manage", user)
|
|
16
|
+
raise ActiveRecord::RecordNotFound if user.id == hr_current_user.id # never one's own quota
|
|
16
17
|
type = LeaveType.find(params[:leave_type_id])
|
|
17
18
|
# Unsanitised, a missing param wrote the adjustment into leave year 0,
|
|
18
19
|
# where no screen can ever show it.
|
|
@@ -84,13 +84,14 @@ module HrLite
|
|
|
84
84
|
# somebody else's report gets a 404 through the scoped relation rather
|
|
85
85
|
# than a 403 — the same shape every employee-tier screen already uses,
|
|
86
86
|
# and it does not confirm that the other person exists.
|
|
87
|
+
# Nobody decides their own request: team scope includes the approver.
|
|
87
88
|
def decidable
|
|
88
|
-
hr_scope(LeaveRequest.all, "leave.approve")
|
|
89
|
+
hr_scope(LeaveRequest.all, "leave.approve").where.not(user_id: hr_current_user.id)
|
|
89
90
|
end
|
|
90
91
|
|
|
91
92
|
def find_decidable = decidable.find(params[:id])
|
|
92
93
|
|
|
93
|
-
def recordable = hr_scope(EmployeeProfile.includes(:user), "leave.approve")
|
|
94
|
+
def recordable = hr_scope(EmployeeProfile.includes(:user), "leave.approve").where.not(user_id: hr_current_user.id)
|
|
94
95
|
end
|
|
95
96
|
end
|
|
96
97
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module HrLite
|
|
2
2
|
module Admin
|
|
3
3
|
class PayrollRunsController < SuperadminController
|
|
4
|
+
# The register carries every bank account: its own key, not just payroll.manage.
|
|
5
|
+
before_action -> { hr_require_permission!("payroll.export", scope: :all) }, only: :register
|
|
4
6
|
def index
|
|
5
7
|
@runs = paginate(PayrollRun.recent_first)
|
|
6
8
|
end
|
|
@@ -82,6 +84,8 @@ module HrLite
|
|
|
82
84
|
|
|
83
85
|
def register_csv(run)
|
|
84
86
|
require "csv"
|
|
87
|
+
# A cell opening with = + - @ runs as a formula; names are staff-typed.
|
|
88
|
+
safe = ->(v) { v.is_a?(String) && v.match?(/\A[=+\-@\t\r]/) ? "'#{v}" : v }
|
|
85
89
|
CSV.generate do |csv|
|
|
86
90
|
csv << [ "Code", "Name", "Days", "LOP", "Gross", "PF", "ESI", "PT", "TDS",
|
|
87
91
|
"Net pay", "Bank", "Account", "IFSC" ]
|
|
@@ -95,7 +99,7 @@ module HrLite
|
|
|
95
99
|
deductions.dig("esi_employee", "amount") || 0, deductions.dig("pt", "amount") || 0,
|
|
96
100
|
deductions.dig("tds", "amount") || 0, slip.net_pay,
|
|
97
101
|
profile&.bank_name, profile&.bank_account_number, profile&.bank_ifsc
|
|
98
|
-
]
|
|
102
|
+
].map(&safe)
|
|
99
103
|
end
|
|
100
104
|
end
|
|
101
105
|
end
|
|
@@ -40,8 +40,9 @@ module HrLite
|
|
|
40
40
|
# A manager decides for their own reports; HR decides for everyone.
|
|
41
41
|
# Scoped through the relation, so reaching for somebody else's report
|
|
42
42
|
# is a 404 rather than a 403 that confirms the person exists.
|
|
43
|
+
# Nobody decides their own request: team scope includes the approver.
|
|
43
44
|
def decidable
|
|
44
|
-
hr_scope(RegularizationRequest.all, "attendance.manage")
|
|
45
|
+
hr_scope(RegularizationRequest.all, "attendance.manage").where.not(user_id: hr_current_user.id)
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
def find_decidable = decidable.find(params[:id])
|
|
@@ -141,9 +141,11 @@ module HrLite
|
|
|
141
141
|
require "csv"
|
|
142
142
|
return "" if rows.empty?
|
|
143
143
|
|
|
144
|
+
# A cell opening with = + - @ runs as a formula; names are staff-typed.
|
|
145
|
+
safe = ->(v) { v.is_a?(String) && v.match?(/\A[=+\-@\t\r]/) ? "'#{v}" : v }
|
|
144
146
|
CSV.generate do |csv|
|
|
145
147
|
csv << rows.first.keys
|
|
146
|
-
rows.each { |row| csv << row.values }
|
|
148
|
+
rows.each { |row| csv << row.values.map(&safe) }
|
|
147
149
|
end
|
|
148
150
|
end
|
|
149
151
|
end
|
|
@@ -62,6 +62,7 @@ module HrLite
|
|
|
62
62
|
def find_manageable
|
|
63
63
|
declaration = TaxDeclaration.find(params[:id])
|
|
64
64
|
hr_require_reach!("tax.manage", declaration.user)
|
|
65
|
+
raise ActiveRecord::RecordNotFound if declaration.user_id == hr_current_user.id # never one's own
|
|
65
66
|
declaration
|
|
66
67
|
end
|
|
67
68
|
|
|
@@ -14,7 +14,7 @@ module HrLite
|
|
|
14
14
|
# leadership audit screen or the policy.changed email.
|
|
15
15
|
MONEY_TIER_TYPES = %w[
|
|
16
16
|
HrLite::Appraisal HrLite::DesignationChange HrLite::SalaryStructure
|
|
17
|
-
HrLite::PayrollRun HrLite::SalarySlip HrLite::PayrollLineItem
|
|
17
|
+
HrLite::PayrollRun HrLite::SalarySlip HrLite::PayrollLineItem HrLite::Loan
|
|
18
18
|
].freeze
|
|
19
19
|
|
|
20
20
|
scope :recent, -> { order(created_at: :desc) }
|
|
@@ -5,6 +5,9 @@ module HrLite
|
|
|
5
5
|
class Document < ApplicationRecord
|
|
6
6
|
include Audited
|
|
7
7
|
|
|
8
|
+
# An Aadhaar, PAN or passport number: encrypted, which also redacts it from audit diffs and their emails.
|
|
9
|
+
encrypts :reference_number
|
|
10
|
+
|
|
8
11
|
VISIBILITIES = %w[self hr money].freeze
|
|
9
12
|
VERIFICATIONS = %w[pending verified rejected].freeze
|
|
10
13
|
|
|
@@ -23,6 +23,7 @@ module HrLite
|
|
|
23
23
|
validate :spent_on_is_not_in_the_future
|
|
24
24
|
validate :within_the_category_cap, on: :create
|
|
25
25
|
validate :receipt_is_present_when_the_category_demands_one
|
|
26
|
+
validate :receipt_is_a_small_pdf_or_image
|
|
26
27
|
|
|
27
28
|
scope :awaiting_reimbursement, -> { where(status: "approved") }
|
|
28
29
|
scope :recent_first, -> { order(spent_on: :desc, id: :desc) }
|
|
@@ -138,5 +139,12 @@ module HrLite
|
|
|
138
139
|
|
|
139
140
|
errors.add(:receipt, "is required for #{category.name}")
|
|
140
141
|
end
|
|
142
|
+
|
|
143
|
+
def receipt_is_a_small_pdf_or_image
|
|
144
|
+
return unless receipt.attached?
|
|
145
|
+
return errors.add(:receipt, "must be a PDF or an image") unless Document::ALLOWED_TYPES.include?(receipt.blob.content_type)
|
|
146
|
+
|
|
147
|
+
errors.add(:receipt, "must be under #{Document::MAX_BYTES / 1.megabyte} MB") if receipt.blob.byte_size > Document::MAX_BYTES
|
|
148
|
+
end
|
|
141
149
|
end
|
|
142
150
|
end
|
|
@@ -25,6 +25,7 @@ module HrLite
|
|
|
25
25
|
|
|
26
26
|
before_validation :cache_days_count, on: :create
|
|
27
27
|
after_create :notify_requested, unless: :recorded_by_hr?
|
|
28
|
+
after_create :notify_team
|
|
28
29
|
|
|
29
30
|
scope :pending, -> { where(status: "pending") }
|
|
30
31
|
scope :approved, -> { where(status: "approved") }
|
|
@@ -171,7 +172,6 @@ module HrLite
|
|
|
171
172
|
return false if insufficient
|
|
172
173
|
|
|
173
174
|
notify_decision("Leave approved")
|
|
174
|
-
notify_team
|
|
175
175
|
true
|
|
176
176
|
end
|
|
177
177
|
|
|
@@ -247,8 +247,8 @@ module HrLite
|
|
|
247
247
|
)
|
|
248
248
|
end
|
|
249
249
|
|
|
250
|
-
# Everyone should know a colleague will be away
|
|
251
|
-
# whole team (matrix row "leave.team_notice"; hosts can mute either
|
|
250
|
+
# Everyone should know a colleague will be away, the moment they apply —
|
|
251
|
+
# bell + email to the whole team (matrix row "leave.team_notice"; hosts can mute either
|
|
252
252
|
# channel). Deliberately excludes the reason: dates are team-relevant,
|
|
253
253
|
# the why is not.
|
|
254
254
|
def notify_team
|
|
@@ -3,36 +3,38 @@ module HrLite
|
|
|
3
3
|
# one source, so they always agree. Sections return Relations (callers
|
|
4
4
|
# paginate or cap as they see fit).
|
|
5
5
|
class OverviewQuery
|
|
6
|
-
|
|
6
|
+
# user_ids nil = everyone; a team-scoped viewer sees only the people they reach.
|
|
7
|
+
def initialize(date: Date.current, user_ids: nil)
|
|
7
8
|
@date = date
|
|
9
|
+
@user_ids = user_ids
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
def pending_requests
|
|
11
|
-
LeaveRequest.pending.includes(:leave_type, :user).order(:created_at)
|
|
13
|
+
reach(LeaveRequest.pending.includes(:leave_type, :user).order(:created_at))
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
def on_leave_today
|
|
15
|
-
LeaveRequest.active_on(@date).includes(:leave_type, :user).order(:start_date)
|
|
17
|
+
reach(LeaveRequest.active_on(@date).includes(:leave_type, :user).order(:start_date))
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
# Comp-off and regularization are approvals too. Counting leave alone made
|
|
19
21
|
# the board announce "All present and accounted for" while both queues
|
|
20
22
|
# still had work in them.
|
|
21
23
|
def pending_comp_offs
|
|
22
|
-
CompOffRequest.pending.includes(:user).recent_first
|
|
24
|
+
reach(CompOffRequest.pending.includes(:user).recent_first)
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
def pending_regularizations
|
|
26
|
-
RegularizationRequest.pending.includes(:user).recent_first
|
|
28
|
+
reach(RegularizationRequest.pending.includes(:user).recent_first)
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
def flagged_today
|
|
30
|
-
AttendanceRecord.for_date(@date).flagged.includes(:user)
|
|
32
|
+
reach(AttendanceRecord.for_date(@date).flagged.includes(:user))
|
|
31
33
|
end
|
|
32
34
|
|
|
33
35
|
# AttendanceCloseJob fills check_out_at, so the days it closed count too.
|
|
34
36
|
def missing_checkout_yesterday
|
|
35
|
-
yesterday = AttendanceRecord.for_date(@date - 1)
|
|
37
|
+
yesterday = reach(AttendanceRecord.for_date(@date - 1))
|
|
36
38
|
yesterday.missing_checkout.or(yesterday.where(regularization_note: AttendanceRecord::AUTO_CHECKOUT_NOTE))
|
|
37
39
|
.includes(:user)
|
|
38
40
|
end
|
|
@@ -49,5 +51,9 @@ module HrLite
|
|
|
49
51
|
def empty?
|
|
50
52
|
kpis.values.all?(&:zero?)
|
|
51
53
|
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def reach(relation) = @user_ids ? relation.where(user_id: @user_ids) : relation
|
|
52
58
|
end
|
|
53
59
|
end
|
|
@@ -135,7 +135,8 @@ module HrLite
|
|
|
135
135
|
def annual_deductions
|
|
136
136
|
declaration = TaxDeclaration.for(@user, @run.period_month)
|
|
137
137
|
return @profile.declared_annual_deductions if declaration.nil?
|
|
138
|
-
|
|
138
|
+
# A rejected declaration lowers nothing: its self-declared total stays editable and unchecked.
|
|
139
|
+
return @profile.declared_annual_deductions if declaration.draft? || declaration.rejected?
|
|
139
140
|
|
|
140
141
|
declaration.allowable_total
|
|
141
142
|
end
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
<% if record&.regularized? %><span class="hrl-badge">Fixed</span><% end %>
|
|
44
44
|
</td>
|
|
45
45
|
<td data-label="">
|
|
46
|
-
<a class="hrl-small" href="<%= hr_lite.admin_attendance_path(employee.id, date: @date, month: @date.strftime("%Y-%m")) %>">
|
|
46
|
+
<a class="hrl-small" href="<%= hr_lite.admin_attendance_path(employee.id, date: @date, month: @date.strftime("%Y-%m")) %>">Edit / half day</a>
|
|
47
47
|
</td>
|
|
48
48
|
</tr>
|
|
49
49
|
<% end %>
|
|
@@ -51,7 +51,7 @@ module HrLite
|
|
|
51
51
|
"resignation.withdrawn" => { bell: true, email: false, leadership_email: true, leadership_bell: false },
|
|
52
52
|
"employee.onboarded" => { bell: true, email: true, leadership_email: true, leadership_bell: false },
|
|
53
53
|
"payroll.draft_ready" => { bell: true, email: true, leadership_email: false, leadership_bell: false },
|
|
54
|
-
"leave.team_notice" => { bell: true, email:
|
|
54
|
+
"leave.team_notice" => { bell: true, email: true, leadership_email: false, leadership_bell: false },
|
|
55
55
|
"comp_off.requested" => { bell: true, email: false, leadership_email: true, leadership_bell: true },
|
|
56
56
|
"comp_off.approved" => { bell: true, email: true, leadership_email: true, leadership_bell: false },
|
|
57
57
|
"comp_off.rejected" => { bell: true, email: true, leadership_email: false, leadership_bell: false },
|
data/lib/hr_lite/version.rb
CHANGED