Class: HrLite::LeaveRequest
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- HrLite::LeaveRequest
- Defined in:
- app/models/hr_lite/leave_request.rb
Constant Summary collapse
- STATUSES =
%w[pending approved rejected cancelled].freeze
Instance Method Summary collapse
-
#approve!(actor:, note: nil) ⇒ Object
Returns false (leaving the request pending) when the balance no longer covers it — the re-check runs inside the row lock so two concurrent approvals cannot overdraw one balance.
- #balance ⇒ Object
- #cancel!(actor:) ⇒ Object
-
#cancellable_by?(actor) ⇒ Boolean
Owner may cancel while pending, or an approved future leave (quota returns automatically because
usedis computed). - #date_range_label ⇒ Object
- #paid? ⇒ Boolean
- #reject!(actor:, note:) ⇒ Object
Instance Method Details
#approve!(actor:, note: nil) ⇒ Object
Returns false (leaving the request pending) when the balance no longer covers it — the re-check runs inside the row lock so two concurrent approvals cannot overdraw one balance.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'app/models/hr_lite/leave_request.rb', line 44 def approve!(actor:, note: nil) insufficient = false transition!("approved", actor, note) do # Serialize on the BALANCE row. `transition!`'s own lock is on this # request, and two pending requests are two different rows — so both # approvals could read the same untouched balance and overdraw it. LeaveBalance.lock_for(user, leave_type, LeaveYear.key_for(start_date)) unless leave_type.unlimited? if insufficient_balance_now? insufficient = true raise ActiveRecord::Rollback end end return false if insufficient notify_decision("Leave approved") notify_team true end |
#balance ⇒ Object
104 105 106 |
# File 'app/models/hr_lite/leave_request.rb', line 104 def balance LeaveBalance.for(user, leave_type, LeaveYear.key_for(start_date)) end |
#cancel!(actor:) ⇒ Object
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'app/models/hr_lite/leave_request.rb', line 79 def cancel!(actor:) was_approved = approved? self.status = "cancelled" self.decided_by_id = actor.id self.decided_at = Time.current save! Notifications.publish( "leave.cancelled", title: "#{HrLite.display_name(user)} cancelled #{was_approved ? 'approved ' : ''}leave " \ "(#{date_range_label})", path: "/admin/leave_requests", bell_to: HrLite.admin_users ) true end |
#cancellable_by?(actor) ⇒ Boolean
Owner may cancel while pending, or an approved future leave (quota
returns automatically because used is computed). Admins may cancel
any pending/approved future leave.
73 74 75 76 77 |
# File 'app/models/hr_lite/leave_request.rb', line 73 def cancellable_by?(actor) return false unless pending? || (approved? && start_date > Date.current) user_id == actor.id || HrLite.admin?(actor) || HrLite.leadership?(actor) end |
#date_range_label ⇒ Object
96 97 98 99 100 101 102 |
# File 'app/models/hr_lite/leave_request.rb', line 96 def date_range_label if start_date == end_date "#{start_date.strftime('%d %b')}#{half_day ? ' (half day)' : ''}" else "#{start_date.strftime('%d %b')} – #{end_date.strftime('%d %b')}" end end |
#paid? ⇒ Boolean
35 36 37 |
# File 'app/models/hr_lite/leave_request.rb', line 35 def paid? leave_type.paid end |
#reject!(actor:, note:) ⇒ Object
64 65 66 67 68 |
# File 'app/models/hr_lite/leave_request.rb', line 64 def reject!(actor:, note:) transition!("rejected", actor, note) notify_decision("Leave rejected") true end |