Class: HrLite::AttendancePuncher

Inherits:
Object
  • Object
show all
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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user, kind, lat, lng, accuracy_m, geo_status) ⇒ AttendancePuncher

Returns a new instance of AttendancePuncher.



15
16
17
18
19
20
21
22
# File 'app/services/hr_lite/attendance_puncher.rb', line 15

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
  @geo_status = geo_status
end

Class Method Details

.call(user:, kind:, lat: nil, lng: nil, accuracy_m: nil, geo_status: "ok") ⇒ Object



11
12
13
# File 'app/services/hr_lite/attendance_puncher.rb', line 11

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

#callObject



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'app/services/hr_lite/attendance_puncher.rb', line 24

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: already_in_message(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