hr_lite 0.14.0 → 0.16.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 +102 -1
- data/README.md +4 -1
- data/app/assets/javascripts/hr_lite/print.js +5 -0
- data/app/assets/stylesheets/hr_lite/hr_lite.css +72 -0
- data/app/controllers/hr_lite/admin/documents_controller.rb +63 -0
- data/app/controllers/hr_lite/admin/employees_controller.rb +1 -1
- data/app/controllers/hr_lite/admin/holidays_controller.rb +7 -0
- data/app/controllers/hr_lite/admin/leave_requests_controller.rb +23 -0
- data/app/controllers/hr_lite/admin/loans_controller.rb +59 -0
- data/app/controllers/hr_lite/admin/policies_controller.rb +29 -0
- data/app/controllers/hr_lite/admin/tax_declarations_controller.rb +75 -0
- data/app/controllers/hr_lite/documents_controller.rb +69 -0
- data/app/controllers/hr_lite/hr_requests_controller.rb +2 -0
- data/app/controllers/hr_lite/id_cards_controller.rb +25 -0
- data/app/controllers/hr_lite/loans_controller.rb +11 -0
- data/app/controllers/hr_lite/tax_declarations_controller.rb +100 -0
- data/app/helpers/hr_lite/application_helper.rb +18 -3
- data/app/models/hr_lite/approval_flow.rb +1 -1
- data/app/models/hr_lite/employee_profile.rb +21 -3
- data/app/models/hr_lite/hr_request.rb +12 -3
- data/app/models/hr_lite/leave_request.rb +15 -4
- data/app/models/hr_lite/policy.rb +15 -0
- data/app/models/hr_lite/setting.rb +1 -1
- data/app/models/hr_lite/tax_declaration.rb +4 -1
- data/app/views/hr_lite/admin/documents/index.html.erb +89 -0
- data/app/views/hr_lite/admin/documents/show.html.erb +42 -0
- data/app/views/hr_lite/admin/employees/_form.html.erb +5 -0
- data/app/views/hr_lite/admin/employees/show.html.erb +1 -0
- data/app/views/hr_lite/admin/hr_requests/show.html.erb +3 -0
- data/app/views/hr_lite/admin/leave_requests/index.html.erb +3 -0
- data/app/views/hr_lite/admin/leave_requests/new.html.erb +35 -0
- data/app/views/hr_lite/admin/loans/index.html.erb +43 -0
- data/app/views/hr_lite/admin/loans/new.html.erb +39 -0
- data/app/views/hr_lite/admin/loans/show.html.erb +52 -0
- data/app/views/hr_lite/admin/policies/index.html.erb +27 -0
- data/app/views/hr_lite/admin/policies/new.html.erb +22 -0
- data/app/views/hr_lite/admin/tax_declarations/index.html.erb +37 -0
- data/app/views/hr_lite/admin/tax_declarations/show.html.erb +54 -0
- data/app/views/hr_lite/documents/index.html.erb +84 -0
- data/app/views/hr_lite/id_cards/show.html.erb +86 -0
- data/app/views/hr_lite/loans/index.html.erb +52 -0
- data/app/views/hr_lite/shared/_bell.html.erb +3 -0
- data/app/views/hr_lite/tax_declarations/show.html.erb +68 -0
- data/app/views/layouts/hr_lite/application.html.erb +2 -0
- data/config/routes.rb +21 -1
- data/db/migrate/20260927100145_add_id_card_fields_to_hr_lite_employee_profiles.rb +6 -0
- data/db/migrate/20260927101031_add_created_by_id_to_hr_lite_leave_requests.rb +5 -0
- data/lib/hr_lite/configuration.rb +4 -1
- data/lib/hr_lite/notifications.rb +4 -3
- data/lib/hr_lite/version.rb +1 -1
- data/lib/hr_lite.rb +1 -0
- metadata +41 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Record a loan" } %>
|
|
2
|
+
<div class="hrl-page__head"><h1 class="hrl-page__title">Record a loan</h1></div>
|
|
3
|
+
|
|
4
|
+
<section class="hrl-card">
|
|
5
|
+
<p class="hrl-muted hrl-small">
|
|
6
|
+
The instalment comes out of the payslip automatically from the month it
|
|
7
|
+
starts, and stops on its own when the balance runs out — the last
|
|
8
|
+
instalment is whatever is left, not a full one.
|
|
9
|
+
</p>
|
|
10
|
+
<%= form_with model: @loan, url: hr_lite.admin_loans_path, method: :post, local: true do |f| %>
|
|
11
|
+
<%= render "hr_lite/shared/form_errors", record: @loan %>
|
|
12
|
+
<div class="hrl-field">
|
|
13
|
+
<%= f.label :user_id, "Employee" %>
|
|
14
|
+
<%= f.select :user_id, options_for_select(HrLite.employees.map { |u| [ hr_display_name(u), u.id ] },
|
|
15
|
+
@loan.user_id) %>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="hrl-field">
|
|
18
|
+
<%= f.label :principal, "Amount lent (₹)" %>
|
|
19
|
+
<%= f.text_field :principal, value: @loan.principal&.to_s("F"), inputmode: "decimal" %>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="hrl-field">
|
|
22
|
+
<%= f.label :monthly_instalment, "Monthly deduction (₹)" %>
|
|
23
|
+
<%= f.text_field :monthly_instalment, value: @loan.monthly_instalment&.to_s("F"),
|
|
24
|
+
inputmode: "decimal" %>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="hrl-field">
|
|
27
|
+
<%= f.label :starts_on, "First deduction in" %>
|
|
28
|
+
<%= f.date_field :starts_on %>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="hrl-field">
|
|
31
|
+
<%= f.label :reason, "What it is for (optional)" %>
|
|
32
|
+
<%= f.text_field :reason %>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="hrl-form-actions">
|
|
35
|
+
<%= f.submit "Record", class: "hrl-btn hrl-btn--primary" %>
|
|
36
|
+
<a class="hrl-btn" href="<%= hr_lite.admin_loans_path %>">Cancel</a>
|
|
37
|
+
</div>
|
|
38
|
+
<% end %>
|
|
39
|
+
</section>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<% content_for(:page_title) { hr_display_name(@loan.user) } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title"><%= hr_display_name(@loan.user) %> — <%= hrl_money(@loan.principal) %></h1>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<section class="hrl-card">
|
|
7
|
+
<div class="hrl-table-wrap">
|
|
8
|
+
<table class="hrl-table hrl-table--stack">
|
|
9
|
+
<tbody>
|
|
10
|
+
<tr><td data-label="Status"><%= @loan.status.humanize %></td></tr>
|
|
11
|
+
<tr><td data-label="Monthly instalment"><%= hrl_money(@loan.monthly_instalment) %></td></tr>
|
|
12
|
+
<tr><td data-label="Starts"><%= @loan.starts_on.strftime("%b %Y") %></td></tr>
|
|
13
|
+
<tr><td data-label="Repaid"><%= hrl_money(@loan.repaid) %></td></tr>
|
|
14
|
+
<tr><td data-label="Outstanding"><strong><%= hrl_money(@loan.outstanding) %></strong></td></tr>
|
|
15
|
+
<% if @loan.reason.present? %><tr><td data-label="Reason"><%= @loan.reason %></td></tr><% end %>
|
|
16
|
+
</tbody>
|
|
17
|
+
</table>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<% if @loan.active? %>
|
|
21
|
+
<div class="hrl-row">
|
|
22
|
+
<%= button_to "Close (stop deductions)", hr_lite.close_admin_loan_path(@loan),
|
|
23
|
+
method: :post, class: "hrl-btn",
|
|
24
|
+
form: { data: { turbo_confirm: "Stop deducting? The outstanding balance stays on record." } } %>
|
|
25
|
+
<%= button_to "Cancel", hr_lite.cancel_admin_loan_path(@loan),
|
|
26
|
+
method: :post, class: "hrl-btn hrl-btn--danger",
|
|
27
|
+
form: { data: { turbo_confirm: "Cancel this loan entirely?" } } %>
|
|
28
|
+
</div>
|
|
29
|
+
<% end %>
|
|
30
|
+
</section>
|
|
31
|
+
|
|
32
|
+
<section class="hrl-card">
|
|
33
|
+
<h2 class="hrl-card__title">Deducted so far</h2>
|
|
34
|
+
<% repayments = @loan.loan_repayments.sort_by(&:period_month).reverse %>
|
|
35
|
+
<% if repayments.empty? %>
|
|
36
|
+
<p class="hrl-muted">Nothing deducted yet.</p>
|
|
37
|
+
<% else %>
|
|
38
|
+
<div class="hrl-table-wrap">
|
|
39
|
+
<table class="hrl-table hrl-table--stack">
|
|
40
|
+
<thead><tr><th>Month</th><th>Amount</th></tr></thead>
|
|
41
|
+
<tbody>
|
|
42
|
+
<% repayments.each do |r| %>
|
|
43
|
+
<tr>
|
|
44
|
+
<td data-label="Month"><%= r.period_month.strftime("%b %Y") %></td>
|
|
45
|
+
<td data-label="Amount" class="hrl-num"><%= hrl_money(r.amount) %></td>
|
|
46
|
+
</tr>
|
|
47
|
+
<% end %>
|
|
48
|
+
</tbody>
|
|
49
|
+
</table>
|
|
50
|
+
</div>
|
|
51
|
+
<% end %>
|
|
52
|
+
</section>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Policies and announcements" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">Policies and announcements</h1>
|
|
4
|
+
<div class="hrl-page__actions"><a class="hrl-btn hrl-btn--primary" href="<%= hr_lite.new_admin_policy_path %>">Publish</a></div>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<section class="hrl-card">
|
|
8
|
+
<% if @policies.empty? %>
|
|
9
|
+
<p class="hrl-muted">Nothing published yet.</p>
|
|
10
|
+
<% else %>
|
|
11
|
+
<div class="hrl-table-wrap">
|
|
12
|
+
<table class="hrl-table hrl-table--stack">
|
|
13
|
+
<thead><tr><th>Title</th><th>Kind</th><th>In force</th><th>Still to acknowledge</th></tr></thead>
|
|
14
|
+
<tbody>
|
|
15
|
+
<% @policies.each do |policy| %>
|
|
16
|
+
<tr>
|
|
17
|
+
<td data-label="Title"><%= policy.title %> <span class="hrl-badge hrl-badge--muted">v<%= policy.version %></span></td>
|
|
18
|
+
<td data-label="Kind"><%= policy.announcement? ? "Announcement" : "Policy" %></td>
|
|
19
|
+
<td data-label="In force"><%= policy.effective_from.strftime("%d %b %Y") %></td>
|
|
20
|
+
<td data-label="Still to acknowledge"><%= policy.announcement? ? "—" : policy.outstanding_for.count %></td>
|
|
21
|
+
</tr>
|
|
22
|
+
<% end %>
|
|
23
|
+
</tbody>
|
|
24
|
+
</table>
|
|
25
|
+
</div>
|
|
26
|
+
<% end %>
|
|
27
|
+
</section>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Publish" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">Publish a policy or announcement</h1>
|
|
4
|
+
<p class="hrl-page__sub">Every employee gets an email and a notification. Reusing a title publishes the next version and asks everyone again.</p>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<section class="hrl-card">
|
|
8
|
+
<%= form_with model: @policy, url: hr_lite.admin_policies_path, method: :post, local: true do |f| %>
|
|
9
|
+
<%= render "hr_lite/shared/form_errors", record: @policy %>
|
|
10
|
+
<div class="hrl-field"><%= f.label :title %><%= f.text_field :title, required: true %></div>
|
|
11
|
+
<div class="hrl-field"><%= f.label :body, "Text" %><%= f.text_area :body, rows: 10, required: true %></div>
|
|
12
|
+
<div class="hrl-field"><%= f.label :effective_from, "In force from" %><%= f.date_field :effective_from %></div>
|
|
13
|
+
<div class="hrl-field hrl-field--check">
|
|
14
|
+
<%= f.check_box :acknowledgement_required %>
|
|
15
|
+
<%= f.label :acknowledgement_required, "Policy — everyone must acknowledge it (untick for an announcement)" %>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="hrl-form-actions">
|
|
18
|
+
<%= f.submit "Publish", class: "hrl-btn hrl-btn--primary" %>
|
|
19
|
+
<a class="hrl-btn" href="<%= hr_lite.admin_policies_path %>">Cancel</a>
|
|
20
|
+
</div>
|
|
21
|
+
<% end %>
|
|
22
|
+
</section>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Tax declarations" } %>
|
|
2
|
+
<div class="hrl-page__head"><h1 class="hrl-page__title">Tax declarations</h1></div>
|
|
3
|
+
|
|
4
|
+
<section class="hrl-card">
|
|
5
|
+
<div class="hrl-row">
|
|
6
|
+
<% HrLite::TaxDeclaration::STATUSES.each do |status| %>
|
|
7
|
+
<a class="hrl-btn <%= "hrl-btn--primary" if @status == status %>"
|
|
8
|
+
href="<%= hr_lite.admin_tax_declarations_path(status: status) %>"><%= status.humanize %></a>
|
|
9
|
+
<% end %>
|
|
10
|
+
</div>
|
|
11
|
+
</section>
|
|
12
|
+
|
|
13
|
+
<section class="hrl-card">
|
|
14
|
+
<% if @declarations.empty? %>
|
|
15
|
+
<p class="hrl-muted">Nothing <%= @status %>.</p>
|
|
16
|
+
<% else %>
|
|
17
|
+
<div class="hrl-table-wrap">
|
|
18
|
+
<table class="hrl-table hrl-table--stack">
|
|
19
|
+
<thead><tr><th>Person</th><th>Year</th><th>Regime</th><th>Claimed</th><th></th></tr></thead>
|
|
20
|
+
<tbody>
|
|
21
|
+
<% @declarations.each do |declaration| %>
|
|
22
|
+
<tr>
|
|
23
|
+
<td data-label="Person"><%= hr_display_name(declaration.user) %></td>
|
|
24
|
+
<td data-label="Year"><%= declaration.label %></td>
|
|
25
|
+
<td data-label="Regime"><%= declaration.regime.humanize %></td>
|
|
26
|
+
<td data-label="Claimed" class="hrl-num"><%= hrl_money(declaration.declared_total) %></td>
|
|
27
|
+
<td data-label="">
|
|
28
|
+
<a class="hrl-small" href="<%= hr_lite.admin_tax_declaration_path(declaration) %>">Check</a>
|
|
29
|
+
</td>
|
|
30
|
+
</tr>
|
|
31
|
+
<% end %>
|
|
32
|
+
</tbody>
|
|
33
|
+
</table>
|
|
34
|
+
</div>
|
|
35
|
+
<% end %>
|
|
36
|
+
<%= hrl_pagination %>
|
|
37
|
+
</section>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<% content_for(:page_title) { hr_display_name(@declaration.user) } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">
|
|
4
|
+
<%= hr_display_name(@declaration.user) %> — <%= @declaration.label %>
|
|
5
|
+
</h1>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<section class="hrl-card">
|
|
9
|
+
<p>
|
|
10
|
+
Regime: <strong><%= @declaration.regime.humanize %></strong> ·
|
|
11
|
+
Status: <%= hrl_request_status_badge(@declaration.status == "verified" ? "approved" : @declaration.status) %>
|
|
12
|
+
</p>
|
|
13
|
+
<p class="hrl-muted hrl-small">
|
|
14
|
+
Until this is verified, payroll deducts against what was CLAIMED — making
|
|
15
|
+
somebody overpay tax all year because paperwork is slow is its own kind of
|
|
16
|
+
wrong. Once verified, only the amounts you enter below count.
|
|
17
|
+
</p>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<%= form_with model: @declaration, url: hr_lite.admin_tax_declaration_path(@declaration),
|
|
21
|
+
method: :patch, scope: :tax_declaration, local: true do |f| %>
|
|
22
|
+
<section class="hrl-card">
|
|
23
|
+
<h2 class="hrl-card__title">What the proof supports</h2>
|
|
24
|
+
<%= f.fields_for :tax_declaration_items do |item| %>
|
|
25
|
+
<div class="hrl-field">
|
|
26
|
+
<%= item.label :verified_amount, item.object.section_label %>
|
|
27
|
+
<span class="hrl-small hrl-muted">
|
|
28
|
+
Claimed <%= hrl_money(item.object.declared_amount) %>
|
|
29
|
+
<%= "— #{item.object.label}" if item.object.label.present? %>
|
|
30
|
+
</span>
|
|
31
|
+
<%= item.text_field :verified_amount,
|
|
32
|
+
value: item.object.verified_amount&.to_s("F"), inputmode: "decimal",
|
|
33
|
+
placeholder: "Verified amount" %>
|
|
34
|
+
</div>
|
|
35
|
+
<% end %>
|
|
36
|
+
<div class="hrl-form-actions">
|
|
37
|
+
<%= f.submit "Record amounts", class: "hrl-btn" %>
|
|
38
|
+
</div>
|
|
39
|
+
</section>
|
|
40
|
+
<% end %>
|
|
41
|
+
|
|
42
|
+
<section class="hrl-card">
|
|
43
|
+
<h2 class="hrl-card__title">Decide</h2>
|
|
44
|
+
<%= button_to "Verify", hr_lite.verify_admin_tax_declaration_path(@declaration),
|
|
45
|
+
method: :post, class: "hrl-btn hrl-btn--primary" %>
|
|
46
|
+
<%= form_with url: hr_lite.reject_admin_tax_declaration_path(@declaration),
|
|
47
|
+
method: :post, local: true do %>
|
|
48
|
+
<div class="hrl-field">
|
|
49
|
+
<%= label_tag :note, "What is missing (required to send back)" %>
|
|
50
|
+
<%= text_field_tag :note %>
|
|
51
|
+
</div>
|
|
52
|
+
<%= submit_tag "Send back", class: "hrl-btn" %>
|
|
53
|
+
<% end %>
|
|
54
|
+
</section>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<% content_for(:page_title) { "My documents" } %>
|
|
2
|
+
<div class="hrl-page__head"><h1 class="hrl-page__title">My documents</h1></div>
|
|
3
|
+
|
|
4
|
+
<% if @expiring.any? %>
|
|
5
|
+
<section class="hrl-card hrl-card--warn">
|
|
6
|
+
<h2 class="hrl-card__title">Expiring soon</h2>
|
|
7
|
+
<ul>
|
|
8
|
+
<% @expiring.each do |doc| %>
|
|
9
|
+
<li><%= doc.title %> — <%= doc.expired? ? "expired" : "expires" %>
|
|
10
|
+
<%= doc.expires_on.strftime("%d %b %Y") %></li>
|
|
11
|
+
<% end %>
|
|
12
|
+
</ul>
|
|
13
|
+
</section>
|
|
14
|
+
<% end %>
|
|
15
|
+
|
|
16
|
+
<section class="hrl-card">
|
|
17
|
+
<h2 class="hrl-card__title">Upload a document</h2>
|
|
18
|
+
<%= form_with model: @document, url: hr_lite.documents_path, method: :post, local: true do |f| %>
|
|
19
|
+
<%= render "hr_lite/shared/form_errors", record: @document %>
|
|
20
|
+
<div class="hrl-field">
|
|
21
|
+
<%= f.label :category %>
|
|
22
|
+
<%= f.select :category,
|
|
23
|
+
options_for_select(HrLite::Document::SENSITIVE_CATEGORIES.map { |c| [ c.humanize, c ] } +
|
|
24
|
+
[ [ "Offer letter", "offer_letter" ], [ "Certificate", "certificate" ],
|
|
25
|
+
[ "Other", "other" ] ], @document.category) %>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="hrl-field"><%= f.label :title %><%= f.text_field :title %></div>
|
|
28
|
+
<div class="hrl-field">
|
|
29
|
+
<%= f.label :reference_number, "Document number (optional)" %>
|
|
30
|
+
<%= f.text_field :reference_number %>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="hrl-field">
|
|
33
|
+
<%= f.label :expires_on, "Expires on (optional — we will remind you)" %>
|
|
34
|
+
<%= f.date_field :expires_on %>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="hrl-field">
|
|
37
|
+
<%= f.label :file, "File (PDF or image, up to 10 MB)" %>
|
|
38
|
+
<%= f.file_field :file %>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="hrl-form-actions"><%= f.submit "Upload", class: "hrl-btn hrl-btn--primary" %></div>
|
|
41
|
+
<% end %>
|
|
42
|
+
</section>
|
|
43
|
+
|
|
44
|
+
<section class="hrl-card">
|
|
45
|
+
<h2 class="hrl-card__title">On file</h2>
|
|
46
|
+
<% if @documents.empty? %>
|
|
47
|
+
<p class="hrl-muted">Nothing uploaded yet.</p>
|
|
48
|
+
<% else %>
|
|
49
|
+
<div class="hrl-table-wrap">
|
|
50
|
+
<table class="hrl-table hrl-table--stack">
|
|
51
|
+
<thead><tr><th>Document</th><th>Expires</th><th>Checked</th><th></th></tr></thead>
|
|
52
|
+
<tbody>
|
|
53
|
+
<% @documents.each do |doc| %>
|
|
54
|
+
<tr>
|
|
55
|
+
<td data-label="Document">
|
|
56
|
+
<strong><%= doc.title %></strong>
|
|
57
|
+
<div class="hrl-small hrl-muted"><%= doc.category.humanize %></div>
|
|
58
|
+
</td>
|
|
59
|
+
<td data-label="Expires">
|
|
60
|
+
<%= doc.expires_on ? doc.expires_on.strftime("%d %b %Y") : "—" %>
|
|
61
|
+
</td>
|
|
62
|
+
<td data-label="Checked">
|
|
63
|
+
<%= hrl_request_status_badge(doc.verification == "verified" ? "approved" : doc.verification) %>
|
|
64
|
+
<% if doc.verification_note.present? %>
|
|
65
|
+
<div class="hrl-small hrl-muted"><%= doc.verification_note %></div>
|
|
66
|
+
<% end %>
|
|
67
|
+
</td>
|
|
68
|
+
<td data-label="">
|
|
69
|
+
<% if doc.file.attached? %>
|
|
70
|
+
<a class="hrl-small" href="<%= hr_lite.download_document_path(doc) %>">Open</a>
|
|
71
|
+
<% end %>
|
|
72
|
+
<% unless doc.verified? %>
|
|
73
|
+
<%= button_to "Remove", hr_lite.document_path(doc), method: :delete,
|
|
74
|
+
class: "hrl-btn",
|
|
75
|
+
form: { data: { turbo_confirm: "Remove #{doc.title}?" } } %>
|
|
76
|
+
<% end %>
|
|
77
|
+
</td>
|
|
78
|
+
</tr>
|
|
79
|
+
<% end %>
|
|
80
|
+
</tbody>
|
|
81
|
+
</table>
|
|
82
|
+
</div>
|
|
83
|
+
<% end %>
|
|
84
|
+
</section>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<% content_for(:page_title) { "ID card" } %>
|
|
2
|
+
<% company = HrLite.config.company.call %>
|
|
3
|
+
<% brand = company[:brand].presence || company[:name].presence || "HR" %>
|
|
4
|
+
<% own = @user == hr_current_user %>
|
|
5
|
+
<div class="hrl-page__head">
|
|
6
|
+
<h1 class="hrl-page__title"><%= own ? "My ID card" : "ID card — #{hr_display_name(@user)}" %></h1>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<% if @profile.nil? %>
|
|
10
|
+
<p class="hrl-card hrl-card--empty"><%= own ? "Your HR profile has not been set up yet, so there is no card to show." : "No HR profile yet." %></p>
|
|
11
|
+
<% else %>
|
|
12
|
+
<% first, rest = hr_display_name(@user).to_s.split(" ", 2) %>
|
|
13
|
+
<div class="hrl-idcard-faces">
|
|
14
|
+
<div class="hrl-idcard-face">
|
|
15
|
+
<span class="hrl-idcard__label">FRONT</span>
|
|
16
|
+
<div class="hrl-idcard">
|
|
17
|
+
<div class="hrl-idcard__band">
|
|
18
|
+
<span class="hrl-idcard__slot"></span>
|
|
19
|
+
<span class="hrl-idcard__logo">
|
|
20
|
+
<% if company[:logo_url].present? %><img src="<%= company[:logo_url] %>" alt="<%= brand %>"><% else %><%= brand %><% end %>
|
|
21
|
+
</span>
|
|
22
|
+
<span class="hrl-idcard__kicker">EMPLOYEE IDENTITY CARD</span>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="hrl-idcard__photo">
|
|
25
|
+
<% if @profile.photo.attached? %>
|
|
26
|
+
<img src="<%= Rails.application.routes.url_helpers.rails_blob_path(@profile.photo, disposition: "inline", only_path: true) %>" alt="<%= hr_display_name(@user) %>">
|
|
27
|
+
<% else %>
|
|
28
|
+
<span><%= hr_display_name(@user).to_s.scan(/\b[[:alpha:]]/).first(2).join.upcase %></span>
|
|
29
|
+
<% end %>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="hrl-idcard__who">
|
|
32
|
+
<div class="hrl-idcard__name"><span class="hrl-idcard__green"><%= first %></span> <span class="hrl-idcard__blue"><%= rest %></span></div>
|
|
33
|
+
<div class="hrl-idcard__role"><%= @profile.designation %></div>
|
|
34
|
+
</div>
|
|
35
|
+
<dl class="hrl-idcard__facts">
|
|
36
|
+
<div><dt>EMPLOYEE ID</dt><dd><%= @profile.employee_code %></dd></div>
|
|
37
|
+
<div><dt>DEPARTMENT</dt><dd><%= @profile.department.presence || "—" %></dd></div>
|
|
38
|
+
<div><dt>BLOOD GROUP</dt><dd class="hrl-idcard__red"><%= @profile.blood_group.presence || "—" %></dd></div>
|
|
39
|
+
<div><dt>JOINED</dt><dd><%= @profile.date_of_joining.strftime("%b %Y") %></dd></div>
|
|
40
|
+
</dl>
|
|
41
|
+
<div class="hrl-idcard__strip"><span></span><span></span><span></span></div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="hrl-idcard-face">
|
|
46
|
+
<span class="hrl-idcard__label">BACK</span>
|
|
47
|
+
<div class="hrl-idcard hrl-idcard--back">
|
|
48
|
+
<span class="hrl-idcard__slot hrl-idcard__slot--back"></span>
|
|
49
|
+
<div class="hrl-idcard__title"><span class="hrl-idcard__green">Scan to</span> <span class="hrl-idcard__blue">Verify</span></div>
|
|
50
|
+
<div class="hrl-idcard__qr"><%= hrl_office_qr_svg(company, brand) %></div>
|
|
51
|
+
<div class="hrl-idcard__code"><%= @profile.employee_code %></div>
|
|
52
|
+
<div class="hrl-idcard__rows">
|
|
53
|
+
<div><span>Emergency contact</span><b><%= @profile.emergency_contact.presence || "—" %></b></div>
|
|
54
|
+
<hr>
|
|
55
|
+
<div><span>Issued</span><b><%= @issued_on&.strftime("%b %Y") || "Not yet issued" %></b></div>
|
|
56
|
+
</div>
|
|
57
|
+
<p class="hrl-idcard__note">This card is the property of <%= brand %>. If found, please return it to our office<%= " or call #{company[:phone]}" if company[:phone].present? %>.</p>
|
|
58
|
+
<div class="hrl-idcard__foot"><b><%= brand %></b><span>AUTHORISED SIGNATORY</span></div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="hrl-idcard-actions">
|
|
64
|
+
<button type="button" class="hrl-btn hrl-btn--primary" data-hrl-print>Print</button>
|
|
65
|
+
<% if own && @print_requested %>
|
|
66
|
+
<p class="hrl-small hrl-muted">Printed card requested on <%= @print_requested.to_date.strftime("%d %b %Y") %>.</p>
|
|
67
|
+
<% elsif own %>
|
|
68
|
+
<%= button_to "Request printed card", hr_lite.hr_requests_path, class: "hrl-btn",
|
|
69
|
+
params: { hr_request: { category: "id_card", subject: "Please print my ID card" } } %>
|
|
70
|
+
<% end %>
|
|
71
|
+
<% if own %>
|
|
72
|
+
<%= form_with url: hr_lite.id_card_photo_path, method: :patch, multipart: true, class: "hrl-row" do |f| %>
|
|
73
|
+
<div class="hrl-field">
|
|
74
|
+
<label for="photo">Change photo</label>
|
|
75
|
+
<%= f.file_field :photo, id: "photo", accept: HrLite::EmployeeProfile::PHOTO_TYPES.join(","), required: true %>
|
|
76
|
+
<p class="hrl-hint">Face centred, plain background. JPG, PNG or WebP, under <%= HrLite::EmployeeProfile::PHOTO_MAX_BYTES / 1.megabyte %> MB.</p>
|
|
77
|
+
</div>
|
|
78
|
+
<%= f.submit "Upload photo", class: "hrl-btn" %>
|
|
79
|
+
<% end %>
|
|
80
|
+
<% end %>
|
|
81
|
+
</div>
|
|
82
|
+
<% end %>
|
|
83
|
+
|
|
84
|
+
<% content_for :scripts do %>
|
|
85
|
+
<%= javascript_include_tag "hr_lite/print", defer: true %>
|
|
86
|
+
<% end %>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<% content_for(:page_title) { "My loans" } %>
|
|
2
|
+
<div class="hrl-page__head"><h1 class="hrl-page__title">Loans & advances</h1></div>
|
|
3
|
+
|
|
4
|
+
<% if @loans.empty? %>
|
|
5
|
+
<section class="hrl-card"><p class="hrl-muted">You have no loans or salary advances.</p></section>
|
|
6
|
+
<% else %>
|
|
7
|
+
<section class="hrl-card">
|
|
8
|
+
<p>Outstanding across everything: <strong><%= hrl_money(@outstanding) %></strong></p>
|
|
9
|
+
</section>
|
|
10
|
+
|
|
11
|
+
<% @loans.each do |loan| %>
|
|
12
|
+
<section class="hrl-card">
|
|
13
|
+
<h2 class="hrl-card__title">
|
|
14
|
+
<%= hrl_money(loan.principal) %>
|
|
15
|
+
<span class="hrl-badge <%= "hrl-badge--muted" unless loan.active? %>"><%= loan.status.humanize %></span>
|
|
16
|
+
</h2>
|
|
17
|
+
<% if loan.reason.present? %><p class="hrl-muted"><%= loan.reason %></p><% end %>
|
|
18
|
+
<div class="hrl-table-wrap">
|
|
19
|
+
<table class="hrl-table hrl-table--stack">
|
|
20
|
+
<tbody>
|
|
21
|
+
<tr><td data-label="Monthly instalment"><%= hrl_money(loan.monthly_instalment) %></td></tr>
|
|
22
|
+
<tr><td data-label="Repaid so far"><%= hrl_money(loan.repaid) %></td></tr>
|
|
23
|
+
<tr><td data-label="Outstanding"><strong><%= hrl_money(loan.outstanding) %></strong></td></tr>
|
|
24
|
+
<tr>
|
|
25
|
+
<td data-label="Next deduction">
|
|
26
|
+
<% amount = loan.instalment_for(@next_month) %>
|
|
27
|
+
<%= amount.positive? ? "#{hrl_money(amount)} in #{@next_month.strftime('%b %Y')}" : "—" %>
|
|
28
|
+
</td>
|
|
29
|
+
</tr>
|
|
30
|
+
</tbody>
|
|
31
|
+
</table>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<% if loan.loan_repayments.any? %>
|
|
35
|
+
<h3 class="hrl-card__title">Already deducted</h3>
|
|
36
|
+
<div class="hrl-table-wrap">
|
|
37
|
+
<table class="hrl-table hrl-table--stack">
|
|
38
|
+
<thead><tr><th>Month</th><th>Amount</th></tr></thead>
|
|
39
|
+
<tbody>
|
|
40
|
+
<% loan.loan_repayments.sort_by(&:period_month).reverse.each do |r| %>
|
|
41
|
+
<tr>
|
|
42
|
+
<td data-label="Month"><%= r.period_month.strftime("%b %Y") %></td>
|
|
43
|
+
<td data-label="Amount" class="hrl-num"><%= hrl_money(r.amount) %></td>
|
|
44
|
+
</tr>
|
|
45
|
+
<% end %>
|
|
46
|
+
</tbody>
|
|
47
|
+
</table>
|
|
48
|
+
</div>
|
|
49
|
+
<% end %>
|
|
50
|
+
</section>
|
|
51
|
+
<% end %>
|
|
52
|
+
<% end %>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Tax declaration" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">Tax declaration — <%= @declaration.label %></h1>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<section class="hrl-card">
|
|
7
|
+
<p class="hrl-muted hrl-small">
|
|
8
|
+
Your whole year's TDS is projected from these numbers, so a declaration
|
|
9
|
+
filed in April spreads the tax evenly instead of landing it in March.
|
|
10
|
+
Claim what you intend to invest; HR records what the proof supports when
|
|
11
|
+
you hand it over.
|
|
12
|
+
</p>
|
|
13
|
+
<p>
|
|
14
|
+
Status: <%= hrl_request_status_badge(@declaration.status == "verified" ? "approved" : @declaration.status) %>
|
|
15
|
+
<% if @declaration.note.present? %>
|
|
16
|
+
<span class="hrl-small hrl-muted">— <%= @declaration.note %></span>
|
|
17
|
+
<% end %>
|
|
18
|
+
</p>
|
|
19
|
+
</section>
|
|
20
|
+
|
|
21
|
+
<%= form_with model: @declaration, url: hr_lite.tax_declaration_path, method: :patch,
|
|
22
|
+
scope: :tax_declaration, local: true do |f| %>
|
|
23
|
+
<%= render "hr_lite/shared/form_errors", record: @declaration %>
|
|
24
|
+
|
|
25
|
+
<section class="hrl-card">
|
|
26
|
+
<div class="hrl-field">
|
|
27
|
+
<%= f.label :regime, "Tax regime" %>
|
|
28
|
+
<%= f.select :regime, [ [ "New regime (lower rates, no deductions)", "new" ],
|
|
29
|
+
[ "Old regime (deductions apply)", "old" ] ] %>
|
|
30
|
+
<span class="hrl-small hrl-muted">
|
|
31
|
+
The deductions below only reduce tax under the old regime.
|
|
32
|
+
</span>
|
|
33
|
+
</div>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<section class="hrl-card">
|
|
37
|
+
<h2 class="hrl-card__title">What you are claiming</h2>
|
|
38
|
+
<%= f.fields_for :tax_declaration_items do |item| %>
|
|
39
|
+
<div class="hrl-field">
|
|
40
|
+
<%= item.hidden_field :section %>
|
|
41
|
+
<%= item.label :declared_amount, item.object.section_label %>
|
|
42
|
+
<%= item.text_field :declared_amount,
|
|
43
|
+
value: item.object.declared_amount&.to_s("F"), inputmode: "decimal" %>
|
|
44
|
+
<%= item.text_field :label, placeholder: "What it is (optional)" %>
|
|
45
|
+
<% if item.object.persisted? && item.object.verified_amount %>
|
|
46
|
+
<span class="hrl-small hrl-muted">
|
|
47
|
+
HR verified <%= hrl_money(item.object.verified_amount) %>
|
|
48
|
+
</span>
|
|
49
|
+
<% end %>
|
|
50
|
+
</div>
|
|
51
|
+
<% end %>
|
|
52
|
+
<p>Total claimed: <strong><%= hrl_money(@declaration.declared_total) %></strong></p>
|
|
53
|
+
<div class="hrl-form-actions">
|
|
54
|
+
<%= f.submit "Save draft", class: "hrl-btn hrl-btn--primary" %>
|
|
55
|
+
</div>
|
|
56
|
+
</section>
|
|
57
|
+
<% end %>
|
|
58
|
+
|
|
59
|
+
<% if @declaration.persisted? && (@declaration.draft? || @declaration.rejected?) %>
|
|
60
|
+
<section class="hrl-card">
|
|
61
|
+
<p class="hrl-muted hrl-small">
|
|
62
|
+
Submitting locks it for HR to check. They can send it back if the proof
|
|
63
|
+
does not match.
|
|
64
|
+
</p>
|
|
65
|
+
<%= button_to "Submit to HR", hr_lite.submit_tax_declaration_path,
|
|
66
|
+
method: :post, class: "hrl-btn hrl-btn--primary" %>
|
|
67
|
+
</section>
|
|
68
|
+
<% end %>
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
|
|
57
57
|
<div class="hrl-side__foot">
|
|
58
58
|
<span class="hrl-side__user" title="<%= hr_current_user&.email %>"><%= hr_display_name(hr_current_user) %></span>
|
|
59
|
+
<%= render "hr_lite/shared/bell" %>
|
|
59
60
|
<% if HrLite.config.back_link %>
|
|
60
61
|
<a class="hrl-side__back" href="<%= HrLite.config.back_link[:url] %>"><%= HrLite.config.back_link[:label] %></a>
|
|
61
62
|
<% end %>
|
|
@@ -71,6 +72,7 @@
|
|
|
71
72
|
<%= company[:name].presence || "HR" %>
|
|
72
73
|
<% end %>
|
|
73
74
|
</a>
|
|
75
|
+
<%= render "hr_lite/shared/bell" %>
|
|
74
76
|
<span class="hrl-appbar__user"><%= hr_display_name(hr_current_user) %></span>
|
|
75
77
|
</header>
|
|
76
78
|
|
data/config/routes.rb
CHANGED
|
@@ -30,12 +30,21 @@ HrLite::Engine.routes.draw do
|
|
|
30
30
|
member { post :acknowledge }
|
|
31
31
|
end
|
|
32
32
|
resources :benefits, only: :index
|
|
33
|
+
resources :documents, only: %i[index create destroy] do
|
|
34
|
+
member { get :download }
|
|
35
|
+
end
|
|
36
|
+
resource :tax_declaration, only: %i[show update], controller: "tax_declarations" do
|
|
37
|
+
post :submit
|
|
38
|
+
end
|
|
39
|
+
resources :loans, only: :index
|
|
33
40
|
get "team", to: "team#show"
|
|
34
41
|
get "org", to: "org#show"
|
|
35
42
|
resources :holidays, only: :index
|
|
36
43
|
get "calendar", to: "calendar#show"
|
|
37
44
|
resources :salary_slips, only: %i[index show]
|
|
38
45
|
resource :employee_profile, only: :show, path: "profile"
|
|
46
|
+
patch "id_card/photo", to: "id_cards#photo", as: :id_card_photo
|
|
47
|
+
get "id_card(/:user_id)", to: "id_cards#show", as: :id_card
|
|
39
48
|
resource :resignation, only: %i[show create], controller: "resignations" do
|
|
40
49
|
post :withdraw
|
|
41
50
|
end
|
|
@@ -43,9 +52,10 @@ HrLite::Engine.routes.draw do
|
|
|
43
52
|
resources :appraisals, only: %i[index show]
|
|
44
53
|
|
|
45
54
|
namespace :admin do
|
|
55
|
+
resources :policies, only: %i[index new create]
|
|
46
56
|
get "overview", to: "overview#index"
|
|
47
57
|
resources :attendances, only: %i[index show update], param: :user_id
|
|
48
|
-
resources :leave_requests, only: %i[index show] do
|
|
58
|
+
resources :leave_requests, only: %i[index show new create] do
|
|
49
59
|
member { post :approve; post :reject; post :cancel }
|
|
50
60
|
end
|
|
51
61
|
resources :leave_balances, only: :index do
|
|
@@ -61,6 +71,16 @@ HrLite::Engine.routes.draw do
|
|
|
61
71
|
resources :assignments, only: %i[create destroy], controller: "role_assignments"
|
|
62
72
|
end
|
|
63
73
|
resources :statutory_rate_cards, only: %i[index new create edit update]
|
|
74
|
+
resources :documents, only: :index do
|
|
75
|
+
member { post :verify; post :reject }
|
|
76
|
+
end
|
|
77
|
+
get "documents/for/:user_id", to: "documents#show", as: :employee_documents
|
|
78
|
+
resources :tax_declarations, only: %i[index show update] do
|
|
79
|
+
member { post :verify; post :reject }
|
|
80
|
+
end
|
|
81
|
+
resources :loans, only: %i[index new create show] do
|
|
82
|
+
member { post :close; post :cancel }
|
|
83
|
+
end
|
|
64
84
|
resources :reports, only: %i[index show]
|
|
65
85
|
resources :expenses, only: :index do
|
|
66
86
|
member { post :approve; post :reject; post :reimburse }
|
|
@@ -9,7 +9,10 @@ module HrLite
|
|
|
9
9
|
:time_zone, :currency_symbol, :on_designation_change,
|
|
10
10
|
:leadership_emails, :extra_stylesheets, :superadmin_emails,
|
|
11
11
|
:mailer_from, :public_url_base, :notification_matrix, :back_link,
|
|
12
|
-
:onboard_user, :offboard_user, :invite_url_for
|
|
12
|
+
:onboard_user, :offboard_user, :invite_url_for,
|
|
13
|
+
# ->(user) { { url:, unread: } } — shows a notifications link in
|
|
14
|
+
# the HR shell; nil (default) shows nothing.
|
|
15
|
+
:notifications
|
|
13
16
|
|
|
14
17
|
attr_reader :leave_year_start_month
|
|
15
18
|
|