PROJECT: Centralised Human Resource System (CHRS)


Overview

Centralised Human Resource System (CHRS) is an application for managing employees within the company. The application is created to assist the Human Resource Department of the company to better manage the employees' information. CHRS is capable of checking work schedule, creating recruitment posts, checking of expenses claims and storage of various information of each employee such as salary, department, position, etc.

Summary of contributions

  • Project Management:

    • Assists in approving, reviewing and merging pull requests.

  • Major enhancement: added modifyPay and modifyAllPay command

    • What it does: allows the user to modify the salary and bonus of the employees. Additionally, the modifyAllPay command enhance the functionality of modifyPay command by enabling the user to modify the salary and bonus of every employees shown in the list.

    • Justification: This feature improves the product significantly because the user can indicate the changes to be made to the employee(s) salary based on values or percentage, and to the bonus based on months of salary.

    • Highlights: These commands enhance the users experience in dealing with payroll due to the functionality to modify the salary and bonus just by indicating the value or percentage for salary, and number of months for bonus without having to calculate themselves which reduce chances for human error.

  • Code contributed: [Reposense Dashboard]

  • Other contributions:

    • Test Case:

      • Wrote additional tests for existing features to increase coverage from 61.3% to 63.2% (#224), 63.2% to 65.0% (#226), 81.4% to 82.2% (#237), 82.2% to 82.7% (#238), 84.9% to 87.9% (#246)

    • Documentation:

      • Updated Developer Guide on modifyPay feature, UI component diagram and instruction for manual testing. (Pull requests: #105, #231, #241)

      • Updated User Guide to ensure information are up to date. (Pull requests: #2, #4, #17, #19, #112, #170, #173, #187)

    • Community:

Contributions to the User Guide

Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

Modify the Salary and/or Bonus of an employee : modifyPay

Modify the Salary and/or Bonus of the employee identified by the index.

Format: modifyPay INDEX [s/SALARY] [b/BONUS] or mp INDEX [s/SALARY] [b/BONUS]

At least one of either Salary or Bonus must be included
Bonus will be replaced by new values with every modification

Examples:

  • modifyPay 1 s/300
    Modify the Salary of employee with index '1' with 300 increment

  • modifyPay 2 b/2
    Modify the Bonus of employee with index '2' to 2 months of the salary

  • modifyPay 3 s/%5 b/1
    Modify the Salary of employee with index '3' by 5% increment and Bonus to 1 month of salary

Modification to Salary and/or Bonus that causes negative values will be rejected
Modification to Salary that goes higher than 999999.99 will be rejected since Salary can only hold a maximum of 6 whole numbers and 2 decimal places.

Modify the Salary and/or Bonus all listed employee(s) : modifyAllPay

Modify the Salary and/or Bonus of all the employee(s) shown on the display list.

Format: modifyAllPay [s/SALARY] [b/BONUS] or map [s/SALARY] [b/BONUS]

At least one of either Salary or Bonus must be included
Bonus will be replaced by new values with every modification

Examples:

  • modifyAllPay s/300
    Modify the Salary of all the listed employee(s) by increment of 300

  • modifyAllPay b/2
    Modify the Bonus of all the listed employee(s) to 1 month of their salary

  • modifyAllPay s/%5 b/1
    Modify the Salary of all the listed employee(s) by 5% increment and Bonus to 1 month of salary

Modification to Salary and/or Bonus that causes negative values to any employee(s) on the list will be rejected
Modification to Salary that goes higher than 999999.99 will be rejected since Salary can only hold a maximum of 6 whole numbers and 2 decimal places.

Employees' income report generation feature [Upcoming in 2.0]

This feature will generate an employee payout report to show the break down of the Salary and Bonus in term of Base Salary, Bonus, Overtime Pay and CPF Contribution. [Upcoming in 2.0]

Contributions to the Developer Guide

Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

Modify Pay Feature

The command modifyPay allows the user to modify the salary and bonus of the employee based on the input.

Current Implementation

The implementation of this command consist of two phases.

Phase 1

The modifyPay function is first supported by ModifyPayCommandParser which implements the Parser interface. The interface parse the arguments parameter, which was inputted by the users to form the ModifyPayCommand object. The arguments will also have their validity checked by the method, isValidSalary and isValidBonus, within respective Classes before being parse into the salary and bonus column which will return an error message when the arguments did not meet the requirements.

Code Snippet of ModifyPayCommandParser that show the parsing of input and the checking validity of the inputs:

    public class ModifyPayCommandParser implements Parser<ModifyPayCommand> {
        private static final double BONUS_UPPER_LIMIT = 24.0;

        public ModifyPayCommand parse(String args) throws ParseException {
            requireNonNull(args);

            ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_SALARY, PREFIX_BONUS);

            Index index;

            try {
                index = ParserUtil.parseIndex(argMultimap.getPreamble());
            } catch (ParseException pe) {
                throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ModifyPayCommand.MESSAGE_USAGE), pe);
            }

            if (!didPrefixAppearOnlyOnce(args)) {
                throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ModifyPayCommand.MESSAGE_USAGE));
            }

            ModSalaryDescriptor modSalaryDescriptor = new ModSalaryDescriptor();

            if (argMultimap.getValue(PREFIX_SALARY).isPresent()) {
                modSalaryDescriptor.setSalary(ParserUtil.parseSalary(argMultimap.getValue(PREFIX_SALARY).get()));
            }

            if (argMultimap.getValue(PREFIX_BONUS).isPresent()) {
                Bonus bonusInput = ParserUtil.parseBonus(argMultimap.getValue(PREFIX_BONUS).get());

                double bonus = Double.parseDouble(argMultimap.getValue(PREFIX_BONUS).get());

                if (bonus > BONUS_UPPER_LIMIT) {
                    throw new ParseException(Bonus.MESSAGE_BONUS_CONSTRAINTS);
                }

                modSalaryDescriptor.setBonus(bonusInput);
            }

            if (!modSalaryDescriptor.isAnyFieldEdited()) {
                throw new ParseException(ModifyPayCommand.MESSAGE_NOT_MODIFIED);
            }

            return new ModifyPayCommand(index, modSalaryDescriptor);
        }
        private boolean didPrefixAppearOnlyOnce(String argument) {
            String salaryPrefix = " " + PREFIX_SALARY.toString();
            String bonusPrefix = " " + PREFIX_BONUS.toString();

            return argument.indexOf(salaryPrefix) == argument.lastIndexOf(salaryPrefix)
                    && argument.indexOf(bonusPrefix) == argument.lastIndexOf(bonusPrefix);
        }
    }

Code snippet of Salary

    public static final String MESSAGE_SALARY_CONSTRAINTS =
            "Salary should only contain numbers, and it should not be blank. Only a maximum of 6 whole numbers and "
            + "2 decimal place are allowed. (Max Salary store value is 999999.99)\n";
    public static final String SALARY_VALIDATION_REGEX = "[%]?[-]?[0-9]{1,6}([.][0-9]{1,2})?";
    public final String value;

    /**
     * Constructs a {@code salary}.
     *
     * @param salary A valid salary.
     */

    public Salary(String salary) {
        requireNonNull(salary);
        checkArgument(isValidSalary(salary), MESSAGE_SALARY_CONSTRAINTS);
        value = salary;
    }

    /**
     * Returns true if a given string is a valid salary.
     */
    public static boolean isValidSalary(String test) {
        return test.matches(SALARY_VALIDATION_REGEX);
    }

Code snippet of Bonus

    public static final String MESSAGE_BONUS_CONSTRAINTS =
            "Bonus should only contain positive numbers and maximum of 2 decimal places from 0 to 24,"
            + " and it should not be blank";
    public static final String BONUS_VALIDATION_REGEX = "(([0-9]{1,7}([.][0-9]{1,2})?)|(1[0-9]{7}([.][0-9]{1,2})?)"
            + "|(2[0-3]([0-9]{1,6})([.][0-9]{1,2})?))";
    public final String value;

    public Bonus(String bonus) {
        requireNonNull(bonus);
        checkArgument(isValidBonus(bonus), MESSAGE_BONUS_CONSTRAINTS);
        value = bonus;
    }

    /**
     * Returns true if a given string is a valid bonus.
     */
    public static boolean isValidBonus(String test) {
        return test.matches(BONUS_VALIDATION_REGEX);
    }
Phase 2

The ModifyPayCommand is being executed in this phase. createModifiedPerson method calls for the edited value from ModifyPayCommandParser to check if there are values being inputted by the users. When it is found to be a null values, createModifiedPerson will take back the original value.

Code snippet of ModifyPayCommand which execute the createModifiedPerson method:

    private static Person createModifiedPerson(Person personToEdit,
                   ModSalaryDescriptor modSalaryDescriptor) throws ParseException {
        assert personToEdit != null;

        EmployeeId updatedEmployeeId = personToEdit.getEmployeeId();
        Name updatedName = personToEdit.getName();
        DateOfBirth updatedDateOfBirth = personToEdit.getDateOfBirth();
        Phone updatedPhone = personToEdit.getPhone();
        Email updatedEmail = personToEdit.getEmail();
        Department updatedDepartment = personToEdit.getDepartment();
        Position updatedPosition = personToEdit.getPosition();
        Address updatedAddress = personToEdit.getAddress();
        Salary updatedSalary = ParserUtil.parseSalary(typeOfSalaryMod(personToEdit, modSalaryDescriptor));
        Bonus updatedBonus = ParserUtil.parseBonus(modifyBonusMonth(personToEdit, modSalaryDescriptor, updatedSalary));

        Set<Tag> updatedTags = personToEdit.getTags();

        return new Person(updatedEmployeeId, updatedName, updatedDateOfBirth, updatedPhone, updatedEmail,
                updatedDepartment, updatedPosition, updatedAddress, updatedSalary, updatedBonus, updatedTags);
    }

The result of ModifyPayCommand is encapsulated as a CommandResult object which is passed back into the UI to reflect the newly modified Salary/Bonus. The sequence diagram below illustrates the operation of modifyPay command:

ModifyPayCommandSequenceDiagram

Employee’s Pay Report: payReport [Upcoming in 2.0]

Generate a report to show the break down of employee’s pay such as base Salary, Bonus, Overtime Pay and CPF Contribution. [Upcoming in 2.0]

Design Considerations

Aspect implementation of modifyPay command:

  • Alternative 1(current choice): A separate command to handle modification of payroll.

    • Pros: Reduce the risk of accidental editing of sensitive information

    • Cons: Additional method and prefixes are required to execute the function

  • Alternative 2: One command to handle all the modifications

    • Pros: Lesser Class to handle different fields

    • Cons: User may accidentally touch on sensitive data and ended up editing them