hr_lite 0.8.0 → 0.11.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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +149 -1
  3. data/app/controllers/hr_lite/approvals_controller.rb +31 -0
  4. data/app/helpers/hr_lite/application_helper.rb +21 -0
  5. data/app/jobs/hr_lite/approval_escalation_job.rb +39 -0
  6. data/app/jobs/hr_lite/document_expiry_job.rb +28 -0
  7. data/app/models/concerns/hr_lite/approvable.rb +48 -0
  8. data/app/models/hr_lite/approval.rb +46 -0
  9. data/app/models/hr_lite/approval_delegation.rb +38 -0
  10. data/app/models/hr_lite/approval_flow.rb +39 -0
  11. data/app/models/hr_lite/approval_step.rb +62 -0
  12. data/app/models/hr_lite/document.rb +95 -0
  13. data/app/models/hr_lite/leave_request.rb +72 -17
  14. data/app/models/hr_lite/loan.rb +65 -0
  15. data/app/models/hr_lite/loan_repayment.rb +14 -0
  16. data/app/models/hr_lite/payroll_line_item.rb +57 -0
  17. data/app/models/hr_lite/payroll_run.rb +34 -0
  18. data/app/models/hr_lite/salary_component.rb +59 -0
  19. data/app/models/hr_lite/tax_declaration.rb +93 -0
  20. data/app/models/hr_lite/tax_declaration_item.rb +32 -0
  21. data/app/services/hr_lite/approval_route.rb +109 -0
  22. data/app/services/hr_lite/slip_builder.rb +76 -3
  23. data/app/views/hr_lite/approvals/index.html.erb +66 -0
  24. data/config/routes.rb +1 -0
  25. data/db/migrate/20260818001151_create_hr_lite_approvals.rb +79 -0
  26. data/db/migrate/20260818002627_create_hr_lite_payroll_components_and_loans.rb +72 -0
  27. data/db/migrate/20260818003616_create_hr_lite_documents_and_declarations.rb +73 -0
  28. data/lib/hr_lite/notifications.rb +10 -0
  29. data/lib/hr_lite/permissions.rb +4 -0
  30. data/lib/hr_lite/role_seeds.rb +6 -3
  31. data/lib/hr_lite/version.rb +1 -1
  32. data/lib/tasks/hr_lite_tasks.rake +2 -1
  33. metadata +21 -1
@@ -33,8 +33,17 @@ module HrLite
33
33
  earnings = Calculators::Proration.call(
34
34
  structure: @structure, payable_days: payable, days_in_month: days_in_month
35
35
  )
36
+ # One-off heads for this month: a bonus, arrears after a backdated
37
+ # revision, a reimbursement. A prorated head is scaled like basic; a
38
+ # bonus is not halved because somebody joined mid-month.
39
+ extra_earnings, extra_deductions = one_off_lines(payable, days_in_month)
40
+ earnings += extra_earnings
41
+
36
42
  gross_earned = earnings.sum(BigDecimal(0)) { |row| row[:amount] }
37
43
  basic_earned = earnings.find { |row| row[:code] == "basic" }&.fetch(:amount) || BigDecimal(0)
44
+ # ESI is assessed on wages, and a reimbursement is not one. Heads that
45
+ # opt out are excluded from the gross it reads.
46
+ esi_gross = gross_earned - earnings.sum(BigDecimal(0)) { |row| row[:excluded_from_esi] ? row[:amount] : 0 }
38
47
 
39
48
  deductions = []
40
49
  employer_costs = {}
@@ -50,7 +59,7 @@ module HrLite
50
59
  )
51
60
  end
52
61
 
53
- esi = Calculators::Esi.call(monthly_gross: esi_reference_gross, gross_earned: gross_earned,
62
+ esi = Calculators::Esi.call(monthly_gross: esi_reference_gross, gross_earned: esi_gross,
54
63
  applicable: @structure.esi_applicable, rates: @rates[:esi])
55
64
  if esi.applicable?
56
65
  deductions << { code: "esi_employee", label: "ESI", amount: esi.employee }
@@ -61,9 +70,13 @@ module HrLite
61
70
  period_month: @run.period_month, rates: @rates[:pt])
62
71
  deductions << { code: "pt", label: "Professional tax", amount: pt } if pt.positive?
63
72
 
73
+ deductions.concat(extra_deductions)
74
+ loan = loan_instalment
75
+ deductions << loan if loan
76
+
64
77
  fy = SalarySlip.fy_to_date(@user, @run.period_month)
65
78
  tds = Calculators::Tds.call(
66
- regime: @profile.tax_regime,
79
+ regime: tax_regime,
67
80
  structure_monthly_gross: @structure.monthly_gross,
68
81
  gross_earned_this_month: gross_earned,
69
82
  # Plus anything paid this FY that this install never ran — a previous
@@ -73,7 +86,7 @@ module HrLite
73
86
  fy_gross_paid: fy[:gross] + Money.d(@profile.fy_opening_gross),
74
87
  fy_tds_paid: fy[:tds] + Money.d(@profile.fy_opening_tds),
75
88
  months_remaining: months_remaining_in_fy,
76
- declared_annual_deductions: @profile.declared_annual_deductions,
89
+ declared_annual_deductions: annual_deductions,
77
90
  rates: @rates[:income_tax],
78
91
  override: @tds_override
79
92
  )
@@ -107,6 +120,66 @@ module HrLite
107
120
 
108
121
  private
109
122
 
123
+ # The declaration if the employee filed one, else the single figure an
124
+ # admin typed on the profile. Old-regime deductions came from that one
125
+ # opaque number, with nothing to show what it was made of and nowhere to
126
+ # keep the proof; a filed declaration supersedes it, and once HR has
127
+ # verified the proof only what the proof supported counts.
128
+ # A declaration names the regime the employee chose for that year, which
129
+ # is a per-YEAR decision; the profile column is only the fallback for
130
+ # somebody who never filed one.
131
+ def tax_regime
132
+ TaxDeclaration.for(@user, @run.period_month)&.regime || @profile.tax_regime
133
+ end
134
+
135
+ def annual_deductions
136
+ declaration = TaxDeclaration.for(@user, @run.period_month)
137
+ return @profile.declared_annual_deductions if declaration.nil?
138
+ return @profile.declared_annual_deductions if declaration.draft?
139
+
140
+ declaration.allowable_total
141
+ end
142
+
143
+ # Splits this month's one-off lines into earnings and deductions, in the
144
+ # component order an install configured. Returns [earnings, deductions].
145
+ def one_off_lines(payable, days_in_month)
146
+ rows = PayrollLineItem.for_slip(@user, @run.period_month)
147
+ .sort_by { |item| [ item.component.position, item.component.label ] }
148
+ earnings = []
149
+ deductions = []
150
+
151
+ rows.each do |item|
152
+ component = item.component
153
+ amount = if component.prorated
154
+ Money.round2(item.amount * (Money.d(payable) / Money.d(days_in_month)))
155
+ else
156
+ item.amount
157
+ end
158
+ next unless amount.positive?
159
+
160
+ row = { code: component.code, label: component.label, amount: amount }
161
+ if component.earning?
162
+ earnings << row.merge(excluded_from_esi: !component.counts_for_esi)
163
+ else
164
+ deductions << row
165
+ end
166
+ end
167
+
168
+ [ earnings, deductions ]
169
+ end
170
+
171
+ # The month's loan instalment, as one deduction line. Computed here and
172
+ # BOOKED only when the run is finalized — a draft is recomputed freely,
173
+ # and booking at compute would take a repayment on every pass.
174
+ def loan_instalment
175
+ total = Loan.active.where(user_id: @user.id).sum(BigDecimal(0)) do |loan|
176
+ loan.instalment_for(@run.period_month)
177
+ end
178
+ return nil unless total.positive?
179
+
180
+ { code: "loan_repayment", label: "Loan repayment", amount: total }
181
+ end
182
+
110
183
  # Months left in the Indian FY (Apr–Mar) including the run month itself:
111
184
  # April = 12, December = 4, January = 3, March = 1. Capped at the exit
112
185
  # month for a leaver, so a final settlement is not spread over months
@@ -0,0 +1,66 @@
1
+ <% content_for(:page_title) { "Waiting on you" } %>
2
+ <div class="hrl-page__head">
3
+ <h1 class="hrl-page__title">Waiting on you</h1>
4
+ </div>
5
+
6
+ <% if @covering_for.any? %>
7
+ <section class="hrl-card">
8
+ <p class="hrl-small hrl-muted">
9
+ You are covering for
10
+ <%= @covering_for.map { |d| hr_display_name(d.from_user) }.to_sentence %>.
11
+ Their approvals appear below alongside your own.
12
+ </p>
13
+ </section>
14
+ <% end %>
15
+
16
+ <section class="hrl-card">
17
+ <% if @approvals.empty? %>
18
+ <p class="hrl-muted">Nothing is waiting on you.</p>
19
+ <% else %>
20
+ <div class="hrl-table-wrap">
21
+ <table class="hrl-table hrl-table--stack">
22
+ <thead><tr><th>Request</th><th>From</th><th>Waiting</th><th></th></tr></thead>
23
+ <tbody>
24
+ <% @approvals.each do |approval| %>
25
+ <% subject = approval.subject %>
26
+ <tr>
27
+ <td data-label="Request">
28
+ <strong><%= approval.label %></strong>
29
+ <% if approval.overdue? %>
30
+ <span class="hrl-badge hrl-badge--bad">Overdue</span>
31
+ <% end %>
32
+ <% if approval.approver_id != hr_current_user.id %>
33
+ <span class="hrl-badge hrl-badge--muted">
34
+ for <%= hr_display_name(approval.approver) %>
35
+ </span>
36
+ <% end %>
37
+ </td>
38
+ <td data-label="From"><%= hr_display_name(subject&.user) %></td>
39
+ <td data-label="Waiting"><%= time_ago_in_words(approval.created_at) %></td>
40
+ <td data-label="">
41
+ <% if (path = hrl_approval_path(approval)) %>
42
+ <a class="hrl-small" href="<%= path %>">Open</a>
43
+ <% end %>
44
+ </td>
45
+ </tr>
46
+ <% end %>
47
+ </tbody>
48
+ </table>
49
+ </div>
50
+ <%= hrl_pagination %>
51
+ <% end %>
52
+ </section>
53
+
54
+ <% if @delegations.any? %>
55
+ <section class="hrl-card">
56
+ <h2 class="hrl-card__title">While you are away</h2>
57
+ <ul>
58
+ <% @delegations.each do |delegation| %>
59
+ <li class="hrl-small">
60
+ <%= hr_display_name(delegation.to_user) %> decides for you,
61
+ <%= delegation.starts_on.strftime("%d %b") %> – <%= delegation.ends_on.strftime("%d %b %Y") %>
62
+ </li>
63
+ <% end %>
64
+ </ul>
65
+ </section>
66
+ <% end %>
data/config/routes.rb CHANGED
@@ -19,6 +19,7 @@ HrLite::Engine.routes.draw do
19
19
  resources :regularization_requests, only: %i[index new create] do
20
20
  member { post :cancel }
21
21
  end
22
+ resources :approvals, only: :index
22
23
  get "team", to: "team#show"
23
24
  get "org", to: "org#show"
24
25
  resources :holidays, only: :index
@@ -0,0 +1,79 @@
1
+ class CreateHrLiteApprovals < ActiveRecord::Migration[8.1]
2
+ # Leave, comp-off, regularization and resignation each grew their own
3
+ # approve/reject/cancel, with their own transition guard and their own
4
+ # notification. Four copies of one idea, and every module still to come
5
+ # (expenses, loans, salary revisions, document verification) would have
6
+ # been a fifth.
7
+ #
8
+ # A FLOW is the definition — "leave needs the manager, then HR". A STEP is
9
+ # one rung of it, naming an approver by RULE rather than by person, so the
10
+ # flow survives somebody leaving. An APPROVAL is one live decision on one
11
+ # record.
12
+ def change
13
+ create_table :hr_lite_approval_flows do |t|
14
+ # The model this flow decides on, e.g. "HrLite::LeaveRequest".
15
+ t.string :subject_type, null: false
16
+ t.string :name, null: false
17
+ t.boolean :active, null: false, default: true
18
+ t.timestamps
19
+ end
20
+ add_index :hr_lite_approval_flows, :subject_type,
21
+ unique: true, where: "active", name: "index_hr_lite_approval_flows_one_active_per_type"
22
+
23
+ create_table :hr_lite_approval_steps do |t|
24
+ t.references :flow, null: false, index: false,
25
+ foreign_key: { to_table: :hr_lite_approval_flows, on_delete: :cascade }
26
+ t.integer :position, null: false
27
+ # manager | manager_of_manager | permission | user
28
+ t.string :approver_rule, null: false
29
+ # permission key for `permission`, user id for `user`, unused otherwise
30
+ t.string :approver_key
31
+ # Everybody on this rung must decide, rather than the first to answer.
32
+ t.boolean :unanimous, null: false, default: false
33
+ # Hours before the step is escalated. Null = never.
34
+ t.integer :sla_hours
35
+ t.timestamps
36
+ end
37
+ add_index :hr_lite_approval_steps, %i[flow_id position], unique: true
38
+ add_check_constraint :hr_lite_approval_steps,
39
+ "approver_rule IN ('manager', 'manager_of_manager', 'permission', 'user')",
40
+ name: "hr_lite_approval_steps_rule_check"
41
+
42
+ create_table :hr_lite_approvals do |t|
43
+ t.string :subject_type, null: false
44
+ t.bigint :subject_id, null: false
45
+ t.references :step, null: false, index: false,
46
+ foreign_key: { to_table: :hr_lite_approval_steps, on_delete: :cascade }
47
+ t.integer :position, null: false
48
+ t.bigint :approver_id, null: false
49
+ # pending | approved | rejected | returned | skipped | cancelled
50
+ t.string :status, null: false, default: "pending"
51
+ t.text :note
52
+ t.datetime :decided_at
53
+ # Who decided in the approver's place, when they had delegated.
54
+ t.bigint :decided_by_id
55
+ t.datetime :escalated_at
56
+ t.timestamps
57
+ end
58
+ add_index :hr_lite_approvals, %i[subject_type subject_id position]
59
+ # One live row per approver per rung: two would let the same person
60
+ # decide twice, or a retry double-count a unanimous step.
61
+ add_index :hr_lite_approvals, %i[subject_type subject_id position approver_id],
62
+ unique: true, name: "index_hr_lite_approvals_one_per_approver_per_step"
63
+ add_index :hr_lite_approvals, :approver_id
64
+ add_check_constraint :hr_lite_approvals,
65
+ "status IN ('pending', 'approved', 'rejected', 'returned', 'skipped', 'cancelled')",
66
+ name: "hr_lite_approvals_status_check"
67
+
68
+ create_table :hr_lite_approval_delegations do |t|
69
+ t.bigint :from_user_id, null: false
70
+ t.bigint :to_user_id, null: false
71
+ t.date :starts_on, null: false
72
+ t.date :ends_on, null: false
73
+ t.string :reason
74
+ t.timestamps
75
+ end
76
+ add_index :hr_lite_approval_delegations, %i[from_user_id starts_on ends_on],
77
+ name: "index_hr_lite_delegations_on_from_and_dates"
78
+ end
79
+ end
@@ -0,0 +1,72 @@
1
+ class CreateHrLitePayrollComponentsAndLoans < ActiveRecord::Migration[8.1]
2
+ # Payroll knew exactly four earning heads — basic, HRA, special allowance,
3
+ # "other" — because they were columns on the salary structure. A bonus, an
4
+ # incentive, an LTA or a reimbursement had nowhere to go but "other", which
5
+ # is one number on a payslip that should have said what it was for.
6
+ #
7
+ # Heads are DATA now. Amounts stay encrypted for the same reason every
8
+ # amount in this engine is: a payslip line is somebody's pay.
9
+ def change
10
+ create_table :hr_lite_salary_components do |t|
11
+ t.string :code, null: false
12
+ t.string :label, null: false
13
+ # earning | deduction
14
+ t.string :kind, null: false, default: "earning"
15
+ # Prorated by attendance like basic, or paid whole (a bonus is not
16
+ # halved because somebody joined mid-month).
17
+ t.boolean :prorated, null: false, default: true
18
+ t.boolean :taxable, null: false, default: true
19
+ # Counts toward the gross ESI is assessed on.
20
+ t.boolean :counts_for_esi, null: false, default: true
21
+ t.integer :position, null: false, default: 0
22
+ t.boolean :active, null: false, default: true
23
+ t.timestamps
24
+ end
25
+ add_index :hr_lite_salary_components, :code, unique: true
26
+ add_check_constraint :hr_lite_salary_components, "kind IN ('earning', 'deduction')",
27
+ name: "hr_lite_salary_components_kind_check"
28
+
29
+ # A one-off on a single month's payslip: a bonus, an incentive, an
30
+ # arrears line after a backdated revision, a reimbursement. Dated to the
31
+ # month rather than to the run, so it survives a run being deleted and
32
+ # recomputed.
33
+ create_table :hr_lite_payroll_line_items do |t|
34
+ t.bigint :user_id, null: false
35
+ t.date :period_month, null: false
36
+ t.references :component, null: false, index: true,
37
+ foreign_key: { to_table: :hr_lite_salary_components }
38
+ t.text :amount, null: false # encrypted BigDecimal
39
+ t.string :note
40
+ t.bigint :created_by_id
41
+ t.timestamps
42
+ end
43
+ add_index :hr_lite_payroll_line_items, %i[user_id period_month]
44
+
45
+ create_table :hr_lite_loans do |t|
46
+ t.bigint :user_id, null: false
47
+ t.text :principal, null: false # encrypted
48
+ t.text :monthly_instalment, null: false # encrypted
49
+ t.date :starts_on, null: false
50
+ t.string :reason
51
+ # active | closed | cancelled
52
+ t.string :status, null: false, default: "active"
53
+ t.bigint :approved_by_id
54
+ t.timestamps
55
+ end
56
+ add_index :hr_lite_loans, %i[user_id status]
57
+ add_check_constraint :hr_lite_loans, "status IN ('active', 'closed', 'cancelled')",
58
+ name: "hr_lite_loans_status_check"
59
+
60
+ # One row per month actually deducted. The outstanding balance is derived
61
+ # from these rather than stored, so a recompute cannot double-count and a
62
+ # deleted run cannot leave the balance wrong.
63
+ create_table :hr_lite_loan_repayments do |t|
64
+ t.references :loan, null: false, index: true,
65
+ foreign_key: { to_table: :hr_lite_loans, on_delete: :cascade }
66
+ t.date :period_month, null: false
67
+ t.text :amount, null: false # encrypted
68
+ t.timestamps
69
+ end
70
+ add_index :hr_lite_loan_repayments, %i[loan_id period_month], unique: true
71
+ end
72
+ end
@@ -0,0 +1,73 @@
1
+ class CreateHrLiteDocumentsAndDeclarations < ActiveRecord::Migration[8.1]
2
+ # Two gaps that were table stakes and simply absent: nowhere to keep an
3
+ # employee's documents, and a tax declaration that was ONE opaque number.
4
+ #
5
+ # `declared_annual_deductions` on the profile is a single figure an admin
6
+ # types in. Nobody can see what it is made of, the employee cannot submit
7
+ # their own, and there is nowhere to put the proof. That is the number the
8
+ # whole year's TDS is projected from.
9
+ def change
10
+ create_table :hr_lite_documents do |t|
11
+ t.bigint :user_id, null: false
12
+ # aadhaar | pan | passport | bank | offer_letter | ... — data, so an
13
+ # install can add its own without a release.
14
+ t.string :category, null: false
15
+ t.string :title, null: false
16
+ t.string :reference_number
17
+ t.date :issued_on
18
+ t.date :expires_on
19
+ # self | hr | money — who may open the file. A payslip and a passport
20
+ # are not the same kind of secret.
21
+ #
22
+ # No column default on purpose: the model derives one from the category
23
+ # when none was chosen, and a column default would make "unset" and
24
+ # "deliberately hr" indistinguishable.
25
+ t.string :visibility, null: false
26
+ # pending | verified | rejected
27
+ t.string :verification, null: false, default: "pending"
28
+ t.bigint :verified_by_id
29
+ t.datetime :verified_at
30
+ t.string :verification_note
31
+ t.bigint :uploaded_by_id
32
+ t.timestamps
33
+ end
34
+ add_index :hr_lite_documents, %i[user_id category]
35
+ add_index :hr_lite_documents, :expires_on
36
+ add_check_constraint :hr_lite_documents, "visibility IN ('self', 'hr', 'money')",
37
+ name: "hr_lite_documents_visibility_check"
38
+ add_check_constraint :hr_lite_documents,
39
+ "verification IN ('pending', 'verified', 'rejected')",
40
+ name: "hr_lite_documents_verification_check"
41
+
42
+ create_table :hr_lite_tax_declarations do |t|
43
+ t.bigint :user_id, null: false
44
+ # 1 April of the financial year it covers.
45
+ t.date :financial_year, null: false
46
+ t.string :regime, null: false, default: "new"
47
+ # draft | submitted | verified | rejected
48
+ t.string :status, null: false, default: "draft"
49
+ t.datetime :submitted_at
50
+ t.bigint :verified_by_id
51
+ t.datetime :verified_at
52
+ t.string :note
53
+ t.timestamps
54
+ end
55
+ add_index :hr_lite_tax_declarations, %i[user_id financial_year], unique: true
56
+ add_check_constraint :hr_lite_tax_declarations,
57
+ "status IN ('draft', 'submitted', 'verified', 'rejected')",
58
+ name: "hr_lite_tax_declarations_status_check"
59
+
60
+ create_table :hr_lite_tax_declaration_items do |t|
61
+ t.references :declaration, null: false, index: true,
62
+ foreign_key: { to_table: :hr_lite_tax_declarations, on_delete: :cascade }
63
+ # 80c | 80d | hra | 80ccd_1b | 24b | other
64
+ t.string :section, null: false
65
+ t.string :label
66
+ t.text :declared_amount, null: false # encrypted
67
+ # What proof actually supported, once HR has looked. Null until then.
68
+ t.text :verified_amount
69
+ t.timestamps
70
+ end
71
+ add_index :hr_lite_tax_declaration_items, %i[declaration_id section]
72
+ end
73
+ end
@@ -24,6 +24,16 @@ module HrLite
24
24
  "appraisal.shared" => { bell: true, email: true, leadership_email: true, leadership_bell: false },
25
25
  "promotion.recorded" => { bell: true, email: true, leadership_email: true, leadership_bell: true },
26
26
  "policy.changed" => { bell: false, email: false, leadership_email: true, leadership_bell: true },
27
+ # An approval past its deadline. Leadership hears too — a decision
28
+ # nobody is making is a process problem, not just one person's inbox.
29
+ "approval.escalated" => { bell: true, email: true, leadership_email: true, leadership_bell: false },
30
+ # Tax declarations. The submission bells whoever verifies them; the
31
+ # decision goes back to the employee, whose year's TDS depends on it.
32
+ "tax.declaration_submitted" => { bell: true, email: false, leadership_email: false, leadership_bell: false },
33
+ "tax.declaration_decided" => { bell: true, email: true, leadership_email: false, leadership_bell: false },
34
+ # A document about to lapse — a passport or a visa is somebody's right
35
+ # to work, and finding out late is the whole problem.
36
+ "document.expiring" => { bell: true, email: true, leadership_email: true, leadership_bell: false },
27
37
  "digest.daily" => { bell: false, email: false, leadership_email: true, leadership_bell: false },
28
38
  "resignation.submitted" => { bell: true, email: false, leadership_email: true, leadership_bell: true },
29
39
  "resignation.accepted" => { bell: true, email: true, leadership_email: true, leadership_bell: false },
@@ -43,6 +43,10 @@ module HrLite
43
43
  "appraisal.manage" => [ "Growth", "Write, share and act on appraisals and promotions" ],
44
44
  "resignation.view" => [ "Lifecycle", "See resignations" ],
45
45
  "resignation.manage" => [ "Lifecycle", "Accept resignations and set the last working day" ],
46
+ "document.view" => [ "Documents", "Open other people's documents" ],
47
+ "document.manage" => [ "Documents", "Upload, verify and open identity and bank documents" ],
48
+ "tax.view" => [ "Payroll", "See tax declarations" ],
49
+ "tax.manage" => [ "Payroll", "Verify tax declarations and the proof behind them" ],
46
50
  "settings.manage" => [ "Administration", "Change company settings, offices and policy" ],
47
51
  "audit.view" => [ "Administration", "Read the audit trail" ],
48
52
  "audit.view_money" => [ "Administration", "Read audit rows about pay, appraisals and promotions" ],
@@ -17,7 +17,7 @@ module HrLite
17
17
  "leave.request" => "self", "leave.view" => "self",
18
18
  "attendance.view" => "self", "payroll.view" => "self",
19
19
  "profile.view" => "self", "appraisal.view" => "self",
20
- "resignation.view" => "self"
20
+ "resignation.view" => "self", "document.view" => "self", "tax.view" => "self"
21
21
  }
22
22
  },
23
23
  Role::MANAGER => {
@@ -35,7 +35,8 @@ module HrLite
35
35
  "leave.request" => "self", "leave.view" => "all", "leave.approve" => "all",
36
36
  "leave.manage" => "all", "attendance.view" => "all", "attendance.manage" => "all",
37
37
  "profile.view" => "all", "payroll.view" => "self",
38
- "appraisal.view" => "self", "resignation.view" => "all"
38
+ "appraisal.view" => "self", "resignation.view" => "all",
39
+ "document.view" => "all", "tax.view" => "self"
39
40
  }
40
41
  },
41
42
  Role::FINANCE => {
@@ -45,6 +46,7 @@ module HrLite
45
46
  "attendance.view" => "all", "profile.view" => "all",
46
47
  "payroll.view" => "all", "payroll.manage" => "all", "payroll.export" => "all",
47
48
  "salary.view" => "all", "salary.manage" => "all",
49
+ "tax.view" => "all", "tax.manage" => "all",
48
50
  "audit.view" => "all", "audit.view_money" => "all"
49
51
  }
50
52
  },
@@ -55,7 +57,8 @@ module HrLite
55
57
  "leave.manage" => "all", "attendance.view" => "all", "attendance.manage" => "all",
56
58
  "profile.view" => "all", "profile.manage" => "all",
57
59
  "resignation.view" => "all", "resignation.manage" => "all",
58
- "settings.manage" => "all", "audit.view" => "all", "payroll.view" => "self"
60
+ "settings.manage" => "all", "audit.view" => "all", "payroll.view" => "self",
61
+ "document.view" => "all"
59
62
  }
60
63
  },
61
64
  Role::SUPER_ADMIN => {
@@ -1,3 +1,3 @@
1
1
  module HrLite
2
- VERSION = "0.8.0"
2
+ VERSION = "0.11.0"
3
3
  end
@@ -4,7 +4,8 @@ namespace :hr_lite do
4
4
  require "hr_lite/seeds"
5
5
  require "hr_lite/role_seeds"
6
6
  require "hr_lite/statutory_seeds"
7
- created = HrLite::Seeds.run! + HrLite::RoleSeeds.call + HrLite::StatutorySeeds.call
7
+ created = HrLite::Seeds.run! + HrLite::RoleSeeds.call + HrLite::StatutorySeeds.call +
8
+ HrLite::SalaryComponent.seed_defaults!
8
9
  puts created.any? ? "hr_lite:seed created: #{created.join(', ')}" : "hr_lite:seed — nothing to do"
9
10
  end
10
11
 
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.8.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kshitiz sinha
@@ -81,6 +81,7 @@ files:
81
81
  - app/controllers/hr_lite/admin/superadmin_controller.rb
82
82
  - app/controllers/hr_lite/application_controller.rb
83
83
  - app/controllers/hr_lite/appraisals_controller.rb
84
+ - app/controllers/hr_lite/approvals_controller.rb
84
85
  - app/controllers/hr_lite/attendance_controller.rb
85
86
  - app/controllers/hr_lite/calendar_controller.rb
86
87
  - app/controllers/hr_lite/career_controller.rb
@@ -99,19 +100,27 @@ files:
99
100
  - app/controllers/hr_lite/users_controller.rb
100
101
  - app/helpers/hr_lite/application_helper.rb
101
102
  - app/jobs/hr_lite/application_job.rb
103
+ - app/jobs/hr_lite/approval_escalation_job.rb
102
104
  - app/jobs/hr_lite/daily_digest_job.rb
105
+ - app/jobs/hr_lite/document_expiry_job.rb
103
106
  - app/jobs/hr_lite/leave_year_rollover_job.rb
104
107
  - app/jobs/hr_lite/payroll_auto_draft_job.rb
105
108
  - app/mailers/hr_lite/application_mailer.rb
106
109
  - app/mailers/hr_lite/event_mailer.rb
110
+ - app/models/concerns/hr_lite/approvable.rb
107
111
  - app/models/concerns/hr_lite/audited.rb
108
112
  - app/models/concerns/hr_lite/encrypted_money.rb
109
113
  - app/models/hr_lite/application_record.rb
110
114
  - app/models/hr_lite/appraisal.rb
115
+ - app/models/hr_lite/approval.rb
116
+ - app/models/hr_lite/approval_delegation.rb
117
+ - app/models/hr_lite/approval_flow.rb
118
+ - app/models/hr_lite/approval_step.rb
111
119
  - app/models/hr_lite/attendance_record.rb
112
120
  - app/models/hr_lite/audit_log.rb
113
121
  - app/models/hr_lite/comp_off_request.rb
114
122
  - app/models/hr_lite/designation_change.rb
123
+ - app/models/hr_lite/document.rb
115
124
  - app/models/hr_lite/employee_profile.rb
116
125
  - app/models/hr_lite/holiday.rb
117
126
  - app/models/hr_lite/kudo.rb
@@ -119,7 +128,10 @@ files:
119
128
  - app/models/hr_lite/leave_balance.rb
120
129
  - app/models/hr_lite/leave_request.rb
121
130
  - app/models/hr_lite/leave_type.rb
131
+ - app/models/hr_lite/loan.rb
132
+ - app/models/hr_lite/loan_repayment.rb
122
133
  - app/models/hr_lite/office_location.rb
134
+ - app/models/hr_lite/payroll_line_item.rb
123
135
  - app/models/hr_lite/payroll_run.rb
124
136
  - app/models/hr_lite/professional_tax_slab.rb
125
137
  - app/models/hr_lite/regularization_request.rb
@@ -127,11 +139,15 @@ files:
127
139
  - app/models/hr_lite/role.rb
128
140
  - app/models/hr_lite/role_assignment.rb
129
141
  - app/models/hr_lite/role_grant.rb
142
+ - app/models/hr_lite/salary_component.rb
130
143
  - app/models/hr_lite/salary_slip.rb
131
144
  - app/models/hr_lite/salary_structure.rb
132
145
  - app/models/hr_lite/setting.rb
133
146
  - app/models/hr_lite/statutory_rate_card_record.rb
147
+ - app/models/hr_lite/tax_declaration.rb
148
+ - app/models/hr_lite/tax_declaration_item.rb
134
149
  - app/services/hr_lite/access.rb
150
+ - app/services/hr_lite/approval_route.rb
135
151
  - app/services/hr_lite/attendance_puncher.rb
136
152
  - app/services/hr_lite/attendance_summary.rb
137
153
  - app/services/hr_lite/calculators/esi.rb
@@ -195,6 +211,7 @@ files:
195
211
  - app/views/hr_lite/admin/statutory_rate_cards/new.html.erb
196
212
  - app/views/hr_lite/appraisals/index.html.erb
197
213
  - app/views/hr_lite/appraisals/show.html.erb
214
+ - app/views/hr_lite/approvals/index.html.erb
198
215
  - app/views/hr_lite/attendance/_month_grid.html.erb
199
216
  - app/views/hr_lite/attendance/_punch_card.html.erb
200
217
  - app/views/hr_lite/attendance/show.html.erb
@@ -263,6 +280,9 @@ files:
263
280
  - db/migrate/20260817183347_create_hr_lite_roles_and_grants.rb
264
281
  - db/migrate/20260817183629_seed_hr_lite_roles_from_email_tiers.rb
265
282
  - db/migrate/20260817190159_create_hr_lite_statutory_rate_cards.rb
283
+ - db/migrate/20260818001151_create_hr_lite_approvals.rb
284
+ - db/migrate/20260818002627_create_hr_lite_payroll_components_and_loans.rb
285
+ - db/migrate/20260818003616_create_hr_lite_documents_and_declarations.rb
266
286
  - lib/generators/hr_lite/install/install_generator.rb
267
287
  - lib/generators/hr_lite/install/templates/AFTER_INSTALL
268
288
  - lib/generators/hr_lite/install/templates/initializer.rb