Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Human Resources and Payroll Management #7943

Open
wants to merge 46 commits into
base: master
Choose a base branch
from

Conversation

jniles
Copy link
Collaborator

@jniles jniles commented Dec 21, 2024

This PR is ready for testing and merge, once #7979 lands.

It's main contribution is a much better logical flow to the payroll code on the server, and address some of the UI complaints multiple_payroll module. Because little was changed in the data model (SQL) or the core of the UI, the tests were not needed to be extensively updated.

The old PR description is found below to denote the journey we've been on in this PR.

Previous PR Description:

This PR reworks the underlying Human Resources and Payroll Management code and procedures to allow for better code readability, maintainability, efficiency and testing. It is part of an effort to rewrite the payment processes to support multiple payroll systems in a unified way (#7752).

All updates and changes are being made a feature branch refactor-payroll-management. This is the PR pulls that branch into master. It will be marked as a draft, and should not be merged until it is marked as needing review.

General Approach

The general approach for this refactor is to:

  1. Improve code quality by using modern JS techniques (async/await, Promise, etc) and removing unused code paths.
  2. Add comments and debug() calls to complex tasks to document code behavior.
  3. Encapsulate common code within bespoke functions to make the code self-documenting (e.g. isBenefitRubric())
  4. Server APIs gained the payroll/ prefix where there might be confusion (e.g. account_config/ -> payroll/account_config/)
  5. Tackle outstanding payroll issues from the issue tracker.

Some notable changes:

  1. The Rubric Configuration interface was unified (Unify Rubric Configuration Modal #7952).
  2. The Function UI gained additional descriptive text (Feature: Display number of employees per Function #7955)

Issues Tackled

The following issues are tacked in part or in whole:

Payroll Concepts and Definitions (as expressed by BHIMA)

For a review of the DRC Human Resources definitions, see #7969. Below, we discuss how these concepts are expressed in BHIMA.

There are four basic types of payroll modifications: benefits, payroll taxes, withholdings, and pension allowances.

  • Benefits are any rubrics that are paid to an employee. In the current implementation of BHIMA payroll, the employee's base salary is classified as a benefit. However, benefits can also be bonuses, additional compensation for family members, etc.
    • A benefits rubric is identified by the flag: is_discount === 0.
  • Payroll Taxes are called "charge sur remuneration" in French. These taxes are paid by the enterprise and are not born by the employee.
    • They are identified by the flags is_employee !== 1, is_discount === 1, is_linked_pension_fund === 0.
  • Withholdings are monies retained/deducted by the institution from the employee's pay. This might be due to paying back salary advances, taking health insurance, or setting aside in an employee savings account.
    • They are identified by is_discount === 1 and is_employee === 1.
  • "Pension Funds" are retirement funds that are deducted from the employee salary.
    • They are identified by is_employee !== 0, is_discount === 1, and is_linked_pension_fund ===1.

Note: we should probably rename "withholdings" to "deductions".

Notable and/or Breaking Changes

  1. Renamed the API for /account_config/ to /payroll/account_config/ since it is used to configure accounts used in the payroll modules.
  2. Setting DEBUG to include payroll:* spews out much more informative information about the transaction process.
  3. The logic for determining different rubric types (e.g. taxes, pension, etc), has been moved to a common.js function and is shared across all commitment types. It also has some rudimentary tests.
  4. commitmentByEmployee() now calls out to relevant calculate*() functions for taxes, withholdings, pension and benefits. This should make the code somewhat more legible.

Testing

We don't have very many payroll tests. In particular, I'm not sure we have tests for all the different types of commitment functions listed in the enterprise settings. Which means that, should this get merged, we cannot be 100% this code will not break something in production.

Ideally, I'm hoping to continue to hack on payroll over the holidays until we get something that is easy to reason about, maintain, and trace if a regression or a bug occurs. However, we probably shouldn't do a release and/or upgrade until we have better test coverage or assurance that the payroll code isn't going to break in mysterious ways at one of the production sites.

Future Work

This PR addresses only about a quarter of the needed payroll refactor. In many ways, it addresses the low-hanging fruit of function signatures and variable hoisting. More in depth refactor is needed in the following areas:

  1. Add functions to determine taxable/nontaxable income to payroll/multiplePayroll/common.js, document them, and test them.
  2. Apply a similar refactor seen here to files and functions in multiplePayrollIndice associated with multiple indexes.
  3. Apply a similar refactor to payrollSettings and associated files.
  4. Rework the commitFunction#dataCommit() function to have a simpler function signature.
  5. Document the different payroll flows in the code.

On thing I noticed while working on this refactor: there are several places where we loop through employees and compute rubric values that are nearly identical. For example, each commitment function (commitment.js, groupedCommitment.js, commitmentByEmployee.js) loops through the employees, but generates slightly different transactions with the results. With the current code structure, this is fine. However, there is likely a way to make some sort of intermediate representation of the data that is common between all three that we can then reuse for all three.

As an example, one can imagine some sort of pseudocode like this:

const employeeRubricAssignments = loopThroughEmployeesAndComputeRubrics(employees, rubrics) 

// if groupedCommitment.js
const transaction = createGroupedVouchers(employeeRubricAssignments);

// if commitment.js
const transaction =createCommitmentVouchers(employeeRubricAssignments);

// if commitmentByEmployee.js
const transaction = createIndividualVouchers(employeeRubricAssignments);

An advantage of this style is that we could then write unit tests for the loopThroughEmployeesAndComputeRubrics() function and ensure that we are correctly expressing the rubrics as transactions. It would make it faster and easier to write a variety of tests for different scenarios (e.g. multiple pension withholdings, increases of greater than 100%, etc).

Payroll Order of Operations

I spoke to Nancy Allan, who guided me through the basic payroll order of operations. They are:

  1. Take the employee base salary.
  2. Compute the taxes due to CNSS (usually ~5%)
  3. Compute the taxes due to IPR (usually ~30%).
  4. Then you can add on housing and transport, which are typically untaxed.
  5. This provides the net salary that you will pay to the employee.
  6. Finally, the company/employer's taxes are calculated on the net salary. This is usually ~13% of the gross salary.

It should be noted that pension and taxes on pension are separate calculations.

Unanswered Questions:

  1. How are payroll taxes that are born by employees, versus those born by the institution, modeled?
  2. Why is there a different classification of rubrics in the "payroll by indice" modules?
  3. How do we model overtime (heurs supplimentaire) using rubrics? Are they automatically calculated?

@jniles jniles force-pushed the refactor-payroll-management branch 2 times, most recently from cd1a9d8 to a10945c Compare January 7, 2025 17:32
@jniles jniles force-pushed the refactor-payroll-management branch 3 times, most recently from 818ac29 to 9b635c8 Compare January 19, 2025 14:17
jniles added 22 commits February 6, 2025 08:27
This commit comments the commitmentByEmployee.js file as a work in
progress towards the major payroll refactor.
Adds detailed comments to the commitment.js file, as well as
refactoring the code in the following ways:
 1. Uses async/await in place of promises for better control flow.
 2. Use switch/case for explicit control flow.
 2. Delete unused variables, consolidate other variables.
Adds comments to the dataCommitment function.
Adds debug comments for the multiple payroll configuration files.  Also
unwraps the function signature for setMultiConfiguration to no longer
take mysterious parameters.
Refactors the payroll settings controller to include additional
comments, debug() calls and reduce code complexity.
Refactors and renames the API for payroll account configurations from
/account_config to /payroll/account_config to prevent confusion.
Updates comments to ensure correctness and usefulness.  Also migrates
the API to using async/await.
Adds documentation comments.
This commit adds the appropriate eslint skips to allow longer line
comments.
Adds comments, debug() calls, and refactors code to reduce side effects.
For example, many totally functions are consolidated into reduce()
functions.
Work-in-progress towards #7791.

Adds description keys to the commitmentByEmployee() function.
Moves rubric filtering and summing functions into a new common.js file
to help with legibility.
Tightens up the commitment.js file by removing the need for external
cost center code and using `const` to ensure that the arrays are not
reassigned.
Refactors the commitment controller to more cleanly trace the payroll
process.
Removes the assignCostCenter function that was previously in the cost
center module.  There is no need for it to be there, since it only deals
with array processing (though the array is a cost center).  Instead, it
is replaced with an anonymous function inline.
Adds unit tests for the common.js modules.
Moves the cost center assignment function to `payroll/common.js` since
it is shared between the groupedCommitment and commitment files.  Also
adds tests for all the common.js functions.
Reduces the complexity of exchange rate lookups across all commitment
functions by converting the exchange rates into an exchange rate map
with O(1) lookup time.  Reduces lookups from O(n^2) to O(n) lookups.server
Finalizes the clean up work on payroll to provide a base for continued
improvement.
Reworks the commitment function signatures to reduce the size of the
function signatures. Still a work in progress.
This commit simplifies the function call signature by removing extra
parameters that are instead attached to the configuration object passed
into the subsequent commitment() calls.
Simplifies the commitmentByEmployee() function to make external calls
to newly implemented functions:

 1. calculateEmployeeBenefits
 2. calculateEmployeeWithholdings
 3. calculateEmployeePayrollTaxes
 4. calculateEmployeePension

 Each function filters the rubrics for their specific rubric type and
 accumulates the values by employee.

 The descriptions have now also been improved to record the rubric
 label, employee information, and payment period information to the
 voucher descriptive text.

 Closes #7791.
jniles added 23 commits February 6, 2025 08:27
Migrates the code that configures an employee for payment to async/await
to reduce verbosity and better control variable scope.
Replaces the function call with the object spread.
Cleans up a few comments and inconsistent variable names noticed during
the review period.
Replaces the `date_embauche` with `hiring_date` for better English
consistency.

Closes #7821.
Enhances the `bhCurrencyInput` with a `horizontal` property that will
allow the currency to render correctly when on a form-horizontal form.
Replaces var with let/const in currency service and passes ESLint.
Moves the rubrics for payroll lower in the "Optional Information"
section for better consistency.  It also cleans up the controller's
eslint issues.
Corrects the spelling of "Human Resources" throughout the application.
Unifies the rubric configuration modal to configure both the label and
checkboxes for applied rubrics at the same time for a more streamlined
configuration.
Migrates the rubric configuration server controllers to use the
`payroll/` prefix in routes.  It also removes the `/setting` suffix from
the routes, opting instead to have an API similar to the cash payments
or invoices API where the items are provided as an array in the main
payment configuration object.

The next step will be to unify the client UI to allow simultaneous
configuration for the payroll rubrics.
Uses a unified interface for the rubric configuration service.
Fixes a race condition that caused buggy behavior in the Tax IPR Config
Modal controller.  Now, the submit button is disabled until all the data
is available to use.
Does not crash when the rubric items are empty.
Block modal submission when required fields are not filled in and ensure
that `unset` tests work to allow creating a modal with empty rubric
fields.
Converts the server side code to async/await, removes duplicate
FunctionService.
Adds a column to the "function registry" to show the number of
employees with that function assigned.
Removes an unused, untranslated chart-modal.html.  No idea where it came
from.
Implements the same style of batch creates used on the account modal for
creating functions.
Removes technical debt by removing duplicate code.
Fixes lint errors in employee modules.
Addresses a number of cosmetic requests discussed in #6097.  Notably,
the English syntax for "nontaxable" and "payslip" have been addressed.
Additionally, the currency exchange rate is not rendered on the payslip
if the currency is the enterprise currency.  However, there is a bug with
the currency rendering on the payslip generator that does not allow the
currency to be properly rendered.  This will be addressed in a future
PR.
@jniles jniles force-pushed the refactor-payroll-management branch from edc1702 to 7111960 Compare February 6, 2025 14:31
@jniles jniles marked this pull request as ready for review February 6, 2025 14:34
Runs `eslint --fix` on the services associated with human resources and
payroll.
@jniles jniles force-pushed the refactor-payroll-management branch from ede1f3b to 509302f Compare February 6, 2025 14:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant