Class: HrLite::AttendancePuncher
- Inherits:
-
Object
- Object
- HrLite::AttendancePuncher
- Defined in:
- app/services/hr_lite/attendance_puncher.rb
Overview
The single write path for employee punches. Race-safe (DB unique index + row lock + in-lock re-check), never blocked by geolocation: a punch without GPS or outside every office radius is recorded AND flagged — denying GPS must not stop anyone from working.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- GEO_STATUSES =
What the browser can legitimately report (geo_punch.js).
%w[ok denied unavailable timeout insecure].freeze
Class Method Summary collapse
Instance Method Summary collapse
- #call ⇒ Object
-
#initialize(user, kind, lat, lng, accuracy_m, geo_status) ⇒ AttendancePuncher
constructor
A new instance of AttendancePuncher.
Constructor Details
#initialize(user, kind, lat, lng, accuracy_m, geo_status) ⇒ AttendancePuncher
Returns a new instance of AttendancePuncher.
18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'app/services/hr_lite/attendance_puncher.rb', line 18 def initialize(user, kind, lat, lng, accuracy_m, geo_status) @user = user @kind = kind @lat = lat.presence @lng = lng.presence @accuracy_m = accuracy_m.presence # Client-supplied and rendered straight into the admin-facing flag note. # Anything outside the browser's real permission states is not evidence: # a blank one (JS never ran) produced "without GPS ()", and a crafted one # let an employee write reassuring text onto their own flag. @geo_status = GEO_STATUSES.include?(geo_status.to_s) ? geo_status.to_s : "unavailable" end |
Class Method Details
.call(user:, kind:, lat: nil, lng: nil, accuracy_m: nil, geo_status: "ok") ⇒ Object
14 15 16 |
# File 'app/services/hr_lite/attendance_puncher.rb', line 14 def self.call(user:, kind:, lat: nil, lng: nil, accuracy_m: nil, geo_status: "ok") new(user, kind.to_sym, lat, lng, accuracy_m, geo_status.to_s).call end |
Instance Method Details
#call ⇒ Object
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'app/services/hr_lite/attendance_puncher.rb', line 31 def call record = resolve_record return Result.new(error: "No open check-in to close.") if record.nil? record.with_lock do case @kind when :check_in return Result.new(record: record, error: (record)) if record.check_in_at.present? stamp(record, :check_in) when :check_out return Result.new(record: record, error: "Check in first.") if record.check_in_at.blank? stamp(record, :check_out) # repeat check-out overwrites: last out wins else return Result.new(error: "Unknown punch #{@kind}.") end apply_geo_flags(record) record.save! end notify_if_flagged(record) Result.new(record: record) end |