hr_lite 0.57.1 → 0.58.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 288b37a483def4fc57b059096e2ee8af708d1245dd2587119855635ea73d9c11
4
- data.tar.gz: 65fe286df5fb17b8b369ab499b2980e3456b69f92b4f823f89c75e1053af7f35
3
+ metadata.gz: 3c02612fcf572f2b42eab074882c2c07b975e9e272b6b94eb58b3a40edb00cdc
4
+ data.tar.gz: a537c7010fc7e5c5bbfc5e85eaa62d4afb4bc5b04eae953500d64ed5f4d1530e
5
5
  SHA512:
6
- metadata.gz: 955e1a2242f0c9585b219c5896b9069253624c9797aaa1028ecc7c068f5e5cde734af64ddfcf4982696a4b066e66bb322ed67e37a46d5b1587d5ff04e4d14ddc
7
- data.tar.gz: 57ef0949c6c9f8e4569b8b0157b212ba52ad19f226541f5977d32f8773e1555e444b91eda89ccdc350df84c7bf9db51c279bfd3830aff994c487286c102a6693
6
+ metadata.gz: 54ca6bb8b70ce33601c8d163caf80556bdec2c653b0360f4d3ff418b0180a14fe2487292ef83499d989c5b064b919857bb083197789fef628bf42f736544a6c5
7
+ data.tar.gz: cf2c5930379f973d7a08870b87e3b3c7c28117e4f09921f5465702117c931195203df20292d4532e4b40ce4708479be770627acf7fa5e0a524791c5c81e1db5a
data/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.58.0] - 2026-10-21
11
+
12
+ Leave and birthdays for the whole team, Keka-style. No migrations. Hosts schedule `HrLite::TeamNoticesJob` daily at 10am.
13
+
14
+ - **Pending leave shows on Home.** "On leave this week" lists pending requests too, tagged "Pending approval". Approval is management's decision, not a reason to hide the leave.
15
+ - **10am "On leave today" email** to every current employee: name and dates, never the leave type. No one out, no email.
16
+ - **10am "Birthdays today" email**, same rules. A 29 Feb birthday falls on 28 Feb outside leap years.
17
+ - **Birthdays this week** card on Home, Monday to Sunday, today marked.
18
+
10
19
  ## [0.57.1] - 2026-10-19
11
20
 
12
21
  Forms in the host's iOS and Android staff apps (D24). No migrations.
data/README.md CHANGED
@@ -384,6 +384,7 @@ whatever you run). Each is idempotent and sends nothing on a quiet day.
384
384
  | Job | Schedule | Purpose |
385
385
  |---|---|---|
386
386
  | `HrLite::DailyDigestJob` | each morning | Leadership digest: who is out today, pending approvals, flagged punches, missing checkouts |
387
+ | `HrLite::TeamNoticesJob` | daily, 10am | Emails every current employee who is on leave today and whose birthday it is; each email is skipped when its list is empty |
387
388
  | `HrLite::PayrollAutoDraftJob` | monthly, on the 1st | Draft and compute the previous month's payroll from attendance, then notify the approvers (`payroll.approve`) and payroll operators |
388
389
  | `HrLite::CaReviewReminderJob` | weekly (it acts only in March) | Tell CA reviewers how many slips and declarations are still unverified before 31 March |
389
390
  | `HrLite::CaReviewOverdueJob` | 31 March, evening | Tell payroll and HR what the CA has not verified, and any flags left open |
@@ -3,9 +3,11 @@ module HrLite
3
3
  def index
4
4
  @latest_kudos = Kudo.recent.includes(:giver, kudo_mentions: :user).limit(3)
5
5
  @awards = Award.current
6
- @out_week = LeaveRequest.approved.overlapping_range(Date.current, Date.current.end_of_week)
6
+ # Pending shows too: approval is management's call, not a reason to hide the leave.
7
+ @out_week = LeaveRequest.where(status: %w[pending approved]).overlapping_range(Date.current, Date.current.end_of_week)
7
8
  .includes(:user).where(user_id: HrLite.active_employees.map(&:id))
8
9
  .sort_by { |leave| hr_display_name(leave.user).downcase }
10
+ @birthdays = EmployeeProfile.birthdays_in(Date.current.all_week)
9
11
  @attention = attention_days
10
12
  @waiting = Approval.pending_for(hr_current_user).count
11
13
  @mine_pending = [ LeaveRequest.includes(:leave_type), RegularizationRequest, CompOffRequest, WfhRequest ]
@@ -0,0 +1,26 @@
1
+ module HrLite
2
+ # 10am email to every current employee: who is on leave today, whose
3
+ # birthday it is. Each goes only when its list has someone on it.
4
+ class TeamNoticesJob < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(date: Date.current)
8
+ staff = HrLite.active_employees(on: date)
9
+ out = LeaveRequest.where(status: %w[pending approved], user_id: staff.map(&:id))
10
+ .overlapping_range(date, date).includes(:user)
11
+ announce("leave.out_today", "On leave today", staff, date,
12
+ out.map { |l| "#{HrLite.display_name(l.user)} — #{l.date_range_label}#{' (pending approval)' if l.pending?}" })
13
+ announce("birthday.today", "Birthdays today", staff, date,
14
+ EmployeeProfile.birthdays_in(date..date).map { |p, _| "Happy birthday, #{HrLite.display_name(p.user)}" })
15
+ end
16
+
17
+ private
18
+
19
+ def announce(event, title, staff, date, lines)
20
+ return if lines.empty?
21
+
22
+ Notifications.publish(event, title: "#{title} — #{date.strftime('%a %-d %b')}", lines: lines.sort,
23
+ email_to: staff, path: "/")
24
+ end
25
+ end
26
+ end
@@ -101,6 +101,19 @@ module HrLite
101
101
  date_of_joining <= date && (date_of_exit.nil? || date_of_exit >= date)
102
102
  end
103
103
 
104
+ # 29 Feb birthdays fall on 28 Feb outside leap years.
105
+ def birthday_in(year)
106
+ day = Date.valid_date?(year, date_of_birth.month, date_of_birth.day) ? date_of_birth.day : 28
107
+ Date.new(year, date_of_birth.month, day)
108
+ end
109
+
110
+ # [profile, day] for current staff whose birthday falls in `range`, earliest first.
111
+ def self.birthdays_in(range)
112
+ where(user_id: HrLite.active_employees(on: range.first).map(&:id)).where.not(date_of_birth: nil).includes(:user)
113
+ .filter_map { |p| (day = range.find { |d| p.birthday_in(d.year) == d }) && [ p, day ] }
114
+ .sort_by { |p, day| [ day, HrLite.display_name(p.user).downcase ] }
115
+ end
116
+
104
117
  def employment_range_in(month)
105
118
  from = [ date_of_joining, month.beginning_of_month ].max
106
119
  to = [ date_of_exit || month.end_of_month, month.end_of_month ].min
@@ -40,6 +40,22 @@
40
40
  <%= render "hr_lite/leave_requests/out_list", leaves: @out_week, empty: "Nobody is on leave this week." %>
41
41
  </section>
42
42
 
43
+ <section class="hrl-card">
44
+ <h2 class="hrl-card__title">Birthdays this week</h2>
45
+ <% if @birthdays.any? %>
46
+ <div class="hrl-feed">
47
+ <% @birthdays.each do |profile, day| %>
48
+ <div class="hrl-feed__item">
49
+ <div class="hrl-feed__body"><strong><%= hr_display_name(profile.user) %></strong>
50
+ <div class="hrl-feed__meta"><%= day == Date.current ? "Today" : day.strftime("%a %-d %b") %></div></div>
51
+ </div>
52
+ <% end %>
53
+ </div>
54
+ <% else %>
55
+ <p class="hrl-muted hrl-small">No birthdays this week.</p>
56
+ <% end %>
57
+ </section>
58
+
43
59
  <% if @awards.any? %>
44
60
  <section class="hrl-card">
45
61
  <h2 class="hrl-card__title">Employee of the month, quarter and year</h2>
@@ -4,7 +4,7 @@
4
4
  <% leaves.first(10).each do |leave| %>
5
5
  <div class="hrl-feed__item">
6
6
  <div class="hrl-feed__body"><%= tag.strong hr_display_name(leave.user), title: "On leave: #{leave.date_range_label}" %>
7
- <div class="hrl-feed__meta"><%= leave.date_range_label %></div></div>
7
+ <div class="hrl-feed__meta"><%= leave.date_range_label %><%= " · Pending approval" if leave.pending? %></div></div>
8
8
  </div>
9
9
  <% end %>
10
10
  </div>
@@ -56,6 +56,9 @@ module HrLite
56
56
  "policy.published" => { bell: true, email: true, leadership_email: false, leadership_bell: false },
57
57
  "holiday.published" => { bell: true, email: true, leadership_email: false, leadership_bell: false },
58
58
  "digest.daily" => { bell: false, email: false, leadership_email: true, leadership_bell: false },
59
+ # 10am team emails (TeamNoticesJob) to every current employee; skipped when the list is empty.
60
+ "leave.out_today" => { bell: false, email: true, leadership_email: false, leadership_bell: false },
61
+ "birthday.today" => { bell: false, email: true, leadership_email: false, leadership_bell: false },
59
62
  "resignation.submitted" => { bell: true, email: false, leadership_email: true, leadership_bell: true },
60
63
  "resignation.accepted" => { bell: true, email: true, leadership_email: true, leadership_bell: false },
61
64
  "resignation.withdrawn" => { bell: true, email: false, leadership_email: true, leadership_bell: false },
@@ -1,3 +1,3 @@
1
1
  module HrLite
2
- VERSION = "0.57.1"
2
+ VERSION = "0.58.0"
3
3
  end
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.57.1
4
+ version: 0.58.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kshitiz sinha
@@ -173,6 +173,7 @@ files:
173
173
  - app/jobs/hr_lite/payroll_auto_draft_job.rb
174
174
  - app/jobs/hr_lite/payroll_compute_job.rb
175
175
  - app/jobs/hr_lite/slips_ready_job.rb
176
+ - app/jobs/hr_lite/team_notices_job.rb
176
177
  - app/jobs/hr_lite/work_mode_expire_job.rb
177
178
  - app/mailers/hr_lite/application_mailer.rb
178
179
  - app/mailers/hr_lite/event_mailer.rb