hr_lite 0.56.0 → 0.57.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 +4 -4
- data/CHANGELOG.md +6 -0
- data/app/assets/javascripts/hr_lite/geo_punch.js +11 -4
- data/app/controllers/hr_lite/admin/attendances_controller.rb +4 -0
- data/app/controllers/hr_lite/attendance_controller.rb +14 -0
- data/app/views/hr_lite/admin/attendances/summary.html.erb +6 -0
- 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: d9104f47987b7a8a7e613fd6d07ba269de6b1f2af1ecdd39eed2684b7a62f891
|
|
4
|
+
data.tar.gz: 3a73e730997fefd65b794b47dc14d0c1d836ac60484ee32c5256f601d4cd8043
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 89aae488789fee0bf918a04c4570ed9186860be2acadbca4647d314bd41a1d6008709d10bd341a1636639534086bac276e335e50b2753b1b02ff49f51e8cb241
|
|
7
|
+
data.tar.gz: b152d8230fcccf6c6eb7dbcfbfc5152258d125fa3083a3dad7c439b1188059d01166f0e8ada8d7a5c25900f2223dbb5f192a8d4a502fee126ff4e8b210ae6dd4
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.57.0] - 2026-10-19
|
|
11
|
+
|
|
12
|
+
No migration.
|
|
13
|
+
|
|
14
|
+
- **Why a day is Absent.** A refused check-in or check-out is now saved with its reason, and the person's attendance summary lists it under that day, for example "Check-in tried 10:09 — refused: You are not within the office location…". When the phone holds a tap because location is blocked or not found, the browser tells the server, and the day shows "Location not picked up on the phone (denied)". A past Absent day with no tries says "No check-in tried". Days before the first refusal was ever saved show neither line, because there is no record to go on. The rows are `attendance.punch_refused` entries in the audit trail.
|
|
15
|
+
|
|
10
16
|
## [0.56.0] - 2026-10-17
|
|
11
17
|
|
|
12
18
|
Owner decisions on the HRMS audit (#133–#138). **Two migrations**: the host runs `bin/rails db:migrate` and refreshes `db/schema.rb`.
|
|
@@ -88,10 +88,17 @@
|
|
|
88
88
|
form.submit();
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
function hold(headline) {
|
|
91
|
+
function hold(headline, status) {
|
|
92
92
|
locating = false;
|
|
93
93
|
if (button) { button.disabled = false; button.removeAttribute("aria-busy"); }
|
|
94
94
|
showHelp(label, headline);
|
|
95
|
+
// Tell the server about the tap, so HR sees why the day has no check-in.
|
|
96
|
+
if (/\/check_(in|out)$/.test(form.action) && navigator.sendBeacon) {
|
|
97
|
+
var data = new FormData(form);
|
|
98
|
+
data.set("held", "1");
|
|
99
|
+
data.set("geo_status", status);
|
|
100
|
+
navigator.sendBeacon(form.action, data);
|
|
101
|
+
}
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
form.addEventListener("submit", function (event) {
|
|
@@ -102,7 +109,7 @@
|
|
|
102
109
|
if (label) { label.hidden = false; label.classList.replace("hrl-errors", "hrl-muted"); label.textContent = "Getting location…"; }
|
|
103
110
|
|
|
104
111
|
if (!navigator.geolocation) {
|
|
105
|
-
if (required) hold("This browser cannot share your location. Open this page in Chrome or Safari.");
|
|
112
|
+
if (required) hold("This browser cannot share your location. Open this page in Chrome or Safari.", "unavailable");
|
|
106
113
|
else send("unavailable");
|
|
107
114
|
return;
|
|
108
115
|
}
|
|
@@ -117,8 +124,8 @@
|
|
|
117
124
|
function (err) {
|
|
118
125
|
// Browsers ask only once; a past "Don't allow" denies silently forever.
|
|
119
126
|
// Hold the punch so it is never filed (and flagged) without GPS.
|
|
120
|
-
if (err && err.code === 1) { hold("Location is blocked. To allow it:"); return; }
|
|
121
|
-
if (required) { hold("Couldn't get your location. Check that location is on:"); return; }
|
|
127
|
+
if (err && err.code === 1) { hold("Location is blocked. To allow it:", "denied"); return; }
|
|
128
|
+
if (required) { hold("Couldn't get your location. Check that location is on:", err && err.code === 3 ? "timeout" : "unavailable"); return; }
|
|
122
129
|
send("timeout");
|
|
123
130
|
},
|
|
124
131
|
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 }
|
|
@@ -141,6 +141,10 @@ module HrLite
|
|
|
141
141
|
range = @period.from..@period.to
|
|
142
142
|
@days = DayStatus.new(user: @employee, range: range)
|
|
143
143
|
@punches = AttendancePunch.where(user_id: @employee.id, date: range).order(:at, :id).group_by(&:date)
|
|
144
|
+
refused = AuditLog.where(action: "attendance.punch_refused")
|
|
145
|
+
@refused = refused.where(subject_type: HrLite.user_klass.name, subject_id: @employee.id, created_at: range.first.beginning_of_day..range.last.end_of_day)
|
|
146
|
+
.order(:created_at).group_by { |log| log.created_at.to_date }
|
|
147
|
+
@tracked_from = refused.minimum(:created_at)&.to_date # before this, a day with no tries proves nothing
|
|
144
148
|
@penalties = AttendancePenalty.active.where(user_id: @employee.id, date: range).group(:date).sum(:minutes)
|
|
145
149
|
@balances = LeaveType.active.where(paid: true).map { |type| LeaveBalance.for(@employee, type, LeaveYear.current_key) }
|
|
146
150
|
@requests = summary_requests(@employee, range)
|
|
@@ -51,6 +51,13 @@ module HrLite
|
|
|
51
51
|
private
|
|
52
52
|
|
|
53
53
|
def punch(kind)
|
|
54
|
+
# The browser held the tap (location blocked or not found): log it, file nothing.
|
|
55
|
+
if params[:held]
|
|
56
|
+
geo = AttendancePuncher::GEO_STATUSES.include?(params[:geo_status]) ? params[:geo_status] : "unavailable"
|
|
57
|
+
log_refused(kind, "Location not picked up on the phone (#{geo})")
|
|
58
|
+
return head(:no_content)
|
|
59
|
+
end
|
|
60
|
+
|
|
54
61
|
field = params.fetch(:field, {}).permit(:mode, :purpose, :place).to_h
|
|
55
62
|
result = AttendancePuncher.call(user: hr_current_user, kind: kind, field: field, travel: params[:place] == "travel",
|
|
56
63
|
save_home: params[:save_home] == "1" || params[:place] == "home", **punch_params.to_h.symbolize_keys)
|
|
@@ -58,11 +65,18 @@ module HrLite
|
|
|
58
65
|
time = Time.current.strftime("%H:%M")
|
|
59
66
|
redirect_to attendance_path, notice: "#{kind == :check_in ? 'Checked in' : 'Checked out'} at #{time}."
|
|
60
67
|
else
|
|
68
|
+
log_refused(kind, result.error)
|
|
61
69
|
flash[:punch_offer] = result.offer.to_s if result.offer
|
|
62
70
|
redirect_to attendance_path, alert: result.error
|
|
63
71
|
end
|
|
64
72
|
end
|
|
65
73
|
|
|
74
|
+
# The person's summary lists these per day, so HR can see why a day is Absent.
|
|
75
|
+
def log_refused(kind, error)
|
|
76
|
+
AuditLog.record!(action: "attendance.punch_refused", subject: hr_current_user, actor: hr_current_user,
|
|
77
|
+
changes: { "kind" => kind.to_s, "error" => error })
|
|
78
|
+
end
|
|
79
|
+
|
|
66
80
|
def punch_params
|
|
67
81
|
params.permit(:lat, :lng, :accuracy_m, :geo_status)
|
|
68
82
|
end
|
|
@@ -45,6 +45,12 @@
|
|
|
45
45
|
<% if punches.size > 2 %><div>All punches: <%= punches.join(" · ") %></div><% end %>
|
|
46
46
|
<% if (minutes = @penalties[date]) %><div><span class="hrl-badge hrl-badge--warn">+<%= minutes %> min</span> penalty</div><% end %>
|
|
47
47
|
<% if rec&.flagged? %><div>Flagged: <%= rec.flag_note %></div><% end %>
|
|
48
|
+
<% @refused[date].to_a.each do |log| %>
|
|
49
|
+
<div><%= log.audited_changes["kind"] == "check_out" ? "Check-out" : "Check-in" %> tried <%= log.created_at.strftime("%H:%M") %> — refused: <%= log.audited_changes["error"] %></div>
|
|
50
|
+
<% end %>
|
|
51
|
+
<% if day.kind == :absent && !not_in_yet && @refused[date].nil? && @tracked_from && date >= @tracked_from %>
|
|
52
|
+
<div class="hrl-muted">No check-in tried</div>
|
|
53
|
+
<% end %>
|
|
48
54
|
</div>
|
|
49
55
|
<% if fix && date <= Date.current %>
|
|
50
56
|
<a class="hrl-btn" href="<%= hr_lite.admin_attendance_path(@employee.id, date: date, month: date.strftime("%Y-%m"), anchor: "regularize") %>">Fix<span class="hrl-sr-only"> <%= date.strftime("%-d %b") %></span></a>
|
data/lib/hr_lite/version.rb
CHANGED