hr_lite 0.51.0 → 0.52.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: 29f3f6694be039bef5c21d9548eaf1aaf8ff7c60191b0e92e2903c9fac7769fe
4
- data.tar.gz: 361260627747e8ca8280401e985ef777cc8b267864fc6ccc0229ddf84670e939
3
+ metadata.gz: 6668e3a20cc3b84ebe77b9ae78ff2c20a12c37af21193d88b5f723a1383a7dca
4
+ data.tar.gz: ec7757fc4f4c0220dd21f05037b4054221a76ae445306c32911a2e71dcdd841d
5
5
  SHA512:
6
- metadata.gz: fbe31ca225879b95e882ee76b5cca183ef535c124c75616ebde7cd350bc03bff252febf09d6e7375f0dcf395ab19593548140667e6aa358b707be8f405c0f6f3
7
- data.tar.gz: 39472b4fffa414eaafcbb2a7fb366354608d79f91f1cabe3e41d1e5a32380a2f7b42460fe3539e726850e163cea5680b5531aa4edf5ab7e79449d16e425e4091
6
+ metadata.gz: 68367c0172ccdf649c52272c4c9256e00ecdd55452f7a3cbecd74a12f58e91966eed0b7e5b03f0d20a4380570a219e1602da4604e09cfa7584af20f78097f664
7
+ data.tar.gz: 6128e7796d5bc897db57b0c5ca1ac6a8dfb2ece9eadecf423f5d28815f1a9f847192845976893843b90cac4927cec61ea2c4221912a05f6d161238e944bb6c19
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.52.0] - 2026-10-16
11
+
12
+ - HR uploads documents for an employee: the person's Documents page (HR → People → a person → Documents) now has "Upload a document", the same form as My documents. Only company-wide profile managers (HR, admin) see it, and never on their own record. The document belongs to the employee, records who uploaded it, and its visibility still follows its category (an Aadhaar, PAN, passport or bank document stays with the money tier).
13
+ - Download links now expire after 5 minutes. Before, the file link behind "Open" never expired, so anyone holding a copied link could fetch the scan without signing in. Every download is still permission-checked and audited first.
14
+
15
+ ## [0.51.1] - 2026-10-16
16
+
17
+ - No "Check in again" once the day is worked in full: after checking out with the person's day done (HR's 8h 30m, less any shorter day), the button is gone and the card reads "Your day is done: 8h 45m worked." The server refuses a check-in again too. A short day still offers it as before.
18
+
10
19
  ## [0.51.0] - 2026-10-16
11
20
 
12
21
  - Shorter day on a repeat: HR or admin give a person 30, 60 or 90 minutes off the working day (so 7h at the least) on a pattern set like a calendar's custom repeat: every N days, every N weeks on chosen weekdays, or every N months on the start's date, from a start date, ending never, on a date, or after N times. No repeat means every day, as before.
@@ -17,10 +17,24 @@ module HrLite
17
17
  end
18
18
 
19
19
  def show
20
- @employee = HrLite.user_klass.find(params[:user_id])
21
- hr_require_reach!("document.view", @employee)
22
- @documents = Document.for_user(@employee).recent_first
23
- @missing = Document::SENSITIVE_CATEGORIES - @documents.map(&:category)
20
+ load_person
21
+ @document = Document.new
22
+ end
23
+
24
+ # HR files a document for someone, e.g. a scan handed over on paper:
25
+ # company-wide profile managers only, and never their own (My documents).
26
+ def create
27
+ load_person
28
+ raise ActiveRecord::RecordNotFound if @employee.id == hr_current_user.id
29
+ return hr_access_denied unless hr_can?("profile.manage", scope: :all)
30
+
31
+ @document = Document.new(params.require(:document).permit(:category, :title, :reference_number, :issued_on, :expires_on, :file)
32
+ .merge(user_id: @employee.id, uploaded_by_id: hr_current_user.id))
33
+ if @document.save
34
+ redirect_to admin_employee_documents_path(@employee.id), notice: "#{@document.title} uploaded."
35
+ else
36
+ render :show, status: :unprocessable_entity
37
+ end
24
38
  end
25
39
 
26
40
  def verify
@@ -44,6 +58,13 @@ module HrLite
44
58
 
45
59
  private
46
60
 
61
+ def load_person
62
+ @employee = HrLite.user_klass.find(params[:user_id])
63
+ hr_require_reach!("document.view", @employee)
64
+ @documents = Document.for_user(@employee).recent_first
65
+ @missing = Document::SENSITIVE_CATEGORIES - @documents.map(&:category)
66
+ end
67
+
47
68
  def require_document_access!
48
69
  hr_require_permission!("document.view", scope: :team)
49
70
  end
@@ -56,7 +56,7 @@ module HrLite
56
56
  # rails_blob_path, not url_for: an engine controller has no default
57
57
  # host and url_for(attachment) raises without one.
58
58
  redirect_to Rails.application.routes.url_helpers.rails_blob_path(
59
- document.file, disposition: "attachment", only_path: true
59
+ document.file, disposition: "attachment", only_path: true, expires_in: 5.minutes # the link dies with the visit
60
60
  ), allow_other_host: false
61
61
  end
62
62
 
@@ -76,8 +76,11 @@ module HrLite
76
76
 
77
77
  def worked_under?(minutes) = !!(minutes && worked_duration && worked_duration < minutes.minutes)
78
78
 
79
- # Check in again after checking out: once a day, inside the hours HR sets in Settings.
79
+ # Check in again after checking out: once a day, inside the hours HR sets in
80
+ # Settings, and only while the day is short (a full day has nothing to add).
80
81
  def recheck_in_open?(at = Time.current)
82
+ return false if required_minutes && !short?
83
+
81
84
  s = Setting.instance
82
85
  now = at.in_time_zone(HrLite.config.time_zone).strftime("%H:%M")
83
86
  check_out_at.present? && rechecked_in_at.nil? && now >= s.recheck_in_from && now < s.recheck_in_until
@@ -41,3 +41,7 @@
41
41
  </div>
42
42
  <% end %>
43
43
  </section>
44
+
45
+ <% if @employee.id != hr_current_user.id && hr_can?("profile.manage", scope: :all) %>
46
+ <%= render "hr_lite/documents/upload_form", document: @document, url: hr_lite.admin_employee_documents_path(@employee.id), open: @document.errors.any? %>
47
+ <% end %>
@@ -108,6 +108,8 @@
108
108
  </button>
109
109
  <p class="hrl-small hrl-muted" data-hrl-geo-status role="status" aria-live="polite"></p>
110
110
  <% end %>
111
+ <% elsif record.required_minutes && !record.short? %>
112
+ <p class="hrl-small hrl-muted">Your day is done: <%= hrl_duration(record.worked_duration) %> worked.</p>
111
113
  <% else %>
112
114
  <p class="hrl-small hrl-muted">Check in again is open once a day, <%= HrLite::Setting.instance.recheck_in_from %>–<%= HrLite::Setting.instance.recheck_in_until %>.</p>
113
115
  <% end %>
@@ -0,0 +1,30 @@
1
+ <%# locals: document, url (where it posts), open (start expanded) — one upload form for My documents and HR's view of a person. %>
2
+ <details class="hrl-card" <%= "open" if open || document.errors.any? %>>
3
+ <summary class="hrl-btn hrl-btn--primary">Upload a document</summary>
4
+ <%= form_with model: document, url: url, method: :post, local: true, class: "hrl-mt" do |f| %>
5
+ <%= render "hr_lite/shared/form_errors", record: document %>
6
+ <div class="hrl-field">
7
+ <%= f.label :category, id: "hrl-doc-category", for: nil %>
8
+ <div class="hrl-chiprow" role="radiogroup" aria-labelledby="hrl-doc-category">
9
+ <% (HrLite::Document::SENSITIVE_CATEGORIES.map { |c| [ c.humanize.sub(/\APan\z/, "PAN"), c ] } +
10
+ [ [ "Offer letter", "offer_letter" ], [ "Certificate", "certificate" ], [ "Other", "other" ] ]).each do |label, value| %>
11
+ <label><%= f.radio_button :category, value, required: true %><span><%= label %></span></label>
12
+ <% end %>
13
+ </div>
14
+ </div>
15
+ <div class="hrl-field"><%= f.label :title %><%= f.text_field :title, required: true %></div>
16
+ <div class="hrl-field">
17
+ <%= f.label :reference_number, "Document number (optional)" %>
18
+ <%= f.text_field :reference_number %>
19
+ </div>
20
+ <div class="hrl-field">
21
+ <%= f.label :expires_on, "Expires on (optional — we will remind you)" %>
22
+ <%= f.date_field :expires_on %>
23
+ </div>
24
+ <div class="hrl-field">
25
+ <%= f.label :file, "File (PDF or image, up to 10 MB)" %>
26
+ <%= f.file_field :file, required: true, accept: "application/pdf,image/*" %>
27
+ </div>
28
+ <div class="hrl-form-actions"><%= f.submit "Upload", class: "hrl-btn hrl-btn--primary" %></div>
29
+ <% end %>
30
+ </details>
@@ -55,32 +55,4 @@
55
55
  <% end %>
56
56
  </section>
57
57
 
58
- <details class="hrl-card" <%= "open" if @documents.empty? || @document.errors.any? %>>
59
- <summary class="hrl-btn hrl-btn--primary">Upload a document</summary>
60
- <%= form_with model: @document, url: hr_lite.documents_path, method: :post, local: true, class: "hrl-mt" do |f| %>
61
- <%= render "hr_lite/shared/form_errors", record: @document %>
62
- <div class="hrl-field">
63
- <%= f.label :category, id: "hrl-doc-category", for: nil %>
64
- <div class="hrl-chiprow" role="radiogroup" aria-labelledby="hrl-doc-category">
65
- <% (HrLite::Document::SENSITIVE_CATEGORIES.map { |c| [ c.humanize.sub(/\APan\z/, "PAN"), c ] } +
66
- [ [ "Offer letter", "offer_letter" ], [ "Certificate", "certificate" ], [ "Other", "other" ] ]).each do |label, value| %>
67
- <label><%= f.radio_button :category, value, required: true %><span><%= label %></span></label>
68
- <% end %>
69
- </div>
70
- </div>
71
- <div class="hrl-field"><%= f.label :title %><%= f.text_field :title, required: true %></div>
72
- <div class="hrl-field">
73
- <%= f.label :reference_number, "Document number (optional)" %>
74
- <%= f.text_field :reference_number %>
75
- </div>
76
- <div class="hrl-field">
77
- <%= f.label :expires_on, "Expires on (optional — we will remind you)" %>
78
- <%= f.date_field :expires_on %>
79
- </div>
80
- <div class="hrl-field">
81
- <%= f.label :file, "File (PDF or image, up to 10 MB)" %>
82
- <%= f.file_field :file, required: true, accept: "application/pdf,image/*" %>
83
- </div>
84
- <div class="hrl-form-actions"><%= f.submit "Upload", class: "hrl-btn hrl-btn--primary" %></div>
85
- <% end %>
86
- </details>
58
+ <%= render "hr_lite/documents/upload_form", document: @document, url: hr_lite.documents_path, open: @documents.empty? %>
data/config/routes.rb CHANGED
@@ -105,6 +105,7 @@ HrLite::Engine.routes.draw do
105
105
  member { post :verify; post :reject }
106
106
  end
107
107
  get "documents/for/:user_id", to: "documents#show", as: :employee_documents
108
+ post "documents/for/:user_id", to: "documents#create"
108
109
  resources :tax_declarations, only: %i[index show update] do
109
110
  member { post :verify; post :reject; post :accept_all; get "proofs/:proof_id", action: :proof, as: :proof }
110
111
  collection { get :overview; get "people/:user_id", action: :person, as: :person }
@@ -1,3 +1,3 @@
1
1
  module HrLite
2
- VERSION = "0.51.0"
2
+ VERSION = "0.52.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.51.0
4
+ version: 0.52.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kshitiz sinha
@@ -369,6 +369,7 @@ files:
369
369
  - app/views/hr_lite/career/show.html.erb
370
370
  - app/views/hr_lite/comp_off_requests/index.html.erb
371
371
  - app/views/hr_lite/comp_off_requests/new.html.erb
372
+ - app/views/hr_lite/documents/_upload_form.html.erb
372
373
  - app/views/hr_lite/documents/index.html.erb
373
374
  - app/views/hr_lite/employee_profiles/show.html.erb
374
375
  - app/views/hr_lite/event_mailer/event.html.erb