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
-
Major enhancement:
AddExpensesCommand andDeleteExpensesCommand-
What it does: The
AddExpensescommand allows user to create an expenses or modify an expenses that already expenses.DeleteExpensescommand allows user to delete an expenses from the expenses list. -
Justification: This feature improves the product significantly because a user can add or modify expenses that the employee wishes to claim.
-
Highlights: User can add or modify an individual employee’s single or multiple types of expenses in a single command. This enhancement enhances the user experience as the user can store new or update existing expenses and access it at any point time.
-
-
Minor enhancement:
-
Added
SelectExpensesCommand to allow user to select any expenses in expenses list -
Added
ClearExpensesCommand to allow user to clear the expenses list.
-
-
Code contributed: [Reposense Dashboard]
-
Other contributions:
-
Project management:
-
Assist in approving, reviewing and merging pull requests
-
-
Test cases:
-
Documentation:
-
Updated Developer Guide on
addExpensesfeature alongside model component and instructions for manual testing (Pull requests: #102, #232, #270) -
Updated User Guide to ensure
addExpenses,deleteExpenses,SelectExpensesandclearExpensescommands alongside the fields added by me are up to date (Pull requests: #114, #154, #174)
-
-
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. |
Add expenses claims under a specific employee : addExpenses
Add new expenses for employee or modify expenses if there already exists an expenses
Format: addExpenses id/EMPLOYEEID [tra/TRAVELEXPENSES] [med/MEDICALEXPENSES] [misc/MISCELLANEOUS]
Or ae id/EMPLOYEEID [tra/TRAVELEXPENSES] [med/MEDICALEXPENSES] [misc/MISCELLANEOUS]
At least one of the fields, Travel Expenses, Medical Expenses, Miscellaneous Expenses must be included.
Examples:
-
ae id/000001 tra/111 med/222 misc/333
Creates a new expenses that contain 111.00, 222.00 and 333.00 for the above fields for employee with employee id '000001'.
Total Expenses will reflect 666.00. -
addExpenses id/000002 med/111 misc/222
Creates a new expenses that contain 111.00, 222.00 for the above fields for employee with employee id '000002'. Expenses will contain 0.00 for fields not included in command.
Total Expenses will reflect 333.00.addExpenses id/000002 tra/111 med/222 misc/-111
Add 111.00, 222.00 and minus 111.00 for the above fields for employee with employee id '000002'.
Total Expenses will reflect 555.00.
Any usage of addExpenses Command that will result in negative value for any fields will be rejected.
|
Delete expenses claims of a specific employee : deleteExpenses
Deletes expenses claim from an employee.
Format: deleteExpenses INDEX or de INDEX
Examples:
-
deleteExpenses 1
Deletes expenses claim from employee with Index '1' in the list.
Select an expenses claim : selectExpenses
Select an expenses based on expenses list index ID. User could use command_alias: 'se'.
Format: selectExpenses INDEX or se INDEX
Examples:
-
selectExpenses 1
Select the expenses with the index of '1' in the list
Clear expenses list : clearExpenses
Clear all expenses at one go. User could use command_alias: 'ce'.
Format: clearExpenses or ce
Expenses Report: expensesReport [Upcoming in 2.0]
Generates an expenses report with the total expenses amount claimed and have not been claimed for a year [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. |
Add Expenses
The expenses feature contains to two commands.
-
The command
addExpensesallows the user to add expenses for each employee.
Current Implementation for addExpenses command
Phase 1
The addExpenses command is handled by AddExpensesCommandParser.
The AddExpensesCommandParser implements the Parser interface, takes in input from the user as a form of argument,
The AddExpensesCommandParser will then check if user input contains too many prefixes or check for multiple entries of
same prefix.
Next, the AddExpensesCommandParser will compute the value of expensesAmount to be added by summing the
the values of travelExpenses, medicalExpenses and miscellaneousExpenses. The AddExpensesCommandParser will then
create the Expenses and EditExpensesDescriptor objects. Other classes will handle validation of the input, check if
it has the correct input format.
The five methods that helps to valid the input are:
-
isValidEmployeeIdfromEmployeeIdclass -
isValidExpensesAmountfromExpensesAmountclass -
isValidTravelExpensesfromTravelExpensesclass -
isValidMedicalExpensesfromMedicalExpensesclass -
isValidMiscellaneousExpensesfromMiscellaneousExpensesclass
| If the input does not comply with the required format, an error message will be shown to the user. |
Code snippet of isValidEmployeeId that shows the checking of the input compliance:
public static final String EMPLOYE_EXPENSES_ID_VALIDATION_REGEX = "\\d{3,}";
/**
* Returns true if a given string is a valid Employee Expenses Id.
*/
public static boolean isValidEmployeeId(String test) {
return test.matches(EMPLOYE_EXPENSES_ID_VALIDATION_REGEX);
}
Code snippet of isValidExpensesAmount that shows the checking of the input compliance:
public static final String EMPLOYE_EXPENSES_AMOUNT_VALIDATION_REGEX = "[-]?[0-9]+(.[0-9]{0,2})?";
/**
* Returns true if a given string is a valid Expenses Amount.
*/
public static boolean isValidExpensesAmount(String test) {
return test.matches(EMPLOYE_EXPENSES_AMOUNT_VALIDATION_REGEX);
}
Phase 2
The AddExpensesCommand will take in the Expenses and EditExpensesDescriptor objects.
The AddExpensesCommand will first check if there exists a person in the person list that has the same employeeId as
Expenses object using the condition (!model.hasEmployeeId(toCheckEmployeeId).
The AddExpensesCommand will next check if there exists an expenses in the expenses list that has the same employeeId
as Expenses object using the condition (!model.hasExpenses(toAddExpenses).
Code snippet of condition that checks the person and expenses list for person and expenses with same employeeId:
if (!model.hasEmployeeId(toCheckEmployeeId)) {
throw new CommandException(MESSAGE_EMPLOYEE_ID_NOT_FOUND);
} else if (!model.hasExpenses(toAddExpenses)) {
-
If Expenses List don’t have expenses with same EmployeeId,
-
The
AddExpensesCommandwill then check if any of the fields will contains negative value or exceed the limit. An exception will be thrown if at least one of the fields contains negative value or exceed the limit. -
The
AddExpensesCommandwill then add the expenses into CHRS.
-
Code snippet of If Expenses List don’t have expenses with same EmployeeId
else if (!model.hasExpenses(toAddExpenses)) {
if (Double.parseDouble(toAddExpenses.getExpensesAmount().toString()) < 0
|| Double.parseDouble(toAddExpenses.getTravelExpenses().toString()) < 0
|| Double.parseDouble(toAddExpenses.getMedicalExpenses().toString()) < 0
|| Double.parseDouble(toAddExpenses.getMiscellaneousExpenses().toString()) < 0) {
throw new CommandException(MESSAGE_NEGATIVE_LEFTOVER);
} else if (Double.parseDouble(toAddExpenses.getExpensesAmount().toString()) > MAX_TOTAL_EXPENSES
|| Double.parseDouble(toAddExpenses.getTravelExpenses().toString()) > MAX_EXPENSES_AMOUNT
|| Double.parseDouble(toAddExpenses.getMedicalExpenses().toString()) > MAX_EXPENSES_AMOUNT
|| Double.parseDouble(toAddExpenses.getMiscellaneousExpenses().toString()) > MAX_EXPENSES_AMOUNT
) {
throw new CommandException(MESSAGE_VALUE_OVER_LIMIT);
} else if (Double.parseDouble(toAddExpenses.getExpensesAmount().toString()) >= 0
&& Double.parseDouble(toAddExpenses.getTravelExpenses().toString()) >= 0
&& Double.parseDouble(toAddExpenses.getMedicalExpenses().toString()) >= 0
&& Double.parseDouble(toAddExpenses.getMiscellaneousExpenses().toString()) >= 0
&& Double.parseDouble(toAddExpenses.getExpensesAmount().toString()) <= MAX_TOTAL_EXPENSES
&& Double.parseDouble(toAddExpenses.getTravelExpenses().toString()) <= MAX_EXPENSES_AMOUNT
&& Double.parseDouble(toAddExpenses.getMedicalExpenses().toString()) <= MAX_EXPENSES_AMOUNT
&& Double.parseDouble(toAddExpenses.getMiscellaneousExpenses().toString()) <= MAX_EXPENSES_AMOUNT
) {
model.addExpenses(toAddExpenses);
model.commitExpensesList();
messageToShow = MESSAGE_SUCCESS;
}
-
If Expenses List have expenses with same EmployeeId,
-
The
AddExpensesCommandwill create anexpensesToEditobject TheAddExpensesCommandwill then callcreateEditedExpensesmethod withexpensesToEditandEditExpensesDescriptorobject as parameters.
-
Code snippet creating expensesToEdit object and calling createdEditedExpenses method
else if (model.hasExpenses(toAddExpenses)) {
EmployeeIdExpensesContainsKeywordsPredicate predicatEmployeeId;
List<String> employeeIdList = new ArrayList<>();
List<Expenses> lastShownListExpenses;
employeeIdList.add(toCheckEmployeeId.getEmployeeId().value);
predicatEmployeeId = new EmployeeIdExpensesContainsKeywordsPredicate(employeeIdList);
model.updateFilteredExpensesList(predicatEmployeeId);
lastShownListExpenses = model.getFilteredExpensesList();
Expenses expensesToEdit = lastShownListExpenses.get(0);
Expenses editedExpenses = createEditedExpenses(expensesToEdit, editExpensesDescriptor);
-
The
createEditedExpensesmethod will callmodifyExpensesAmount,modifyTravelExpenses,modifyMedicalExpenses,modifyMiscellaneousExpenses, methods withexpensesToEditandEditExpensesDescriptorobject as parameter.
Code snippet createdEditedExpenses method
private Expenses createEditedExpenses(Expenses expensesToEdit, EditExpensesDescriptor
editExpensesDescriptor) {
assert expensesToEdit != null;
ExpensesAmount updatedExpensesAmount = null;
TravelExpenses updatedTravelExpenses = null;
MedicalExpenses updatedMedicalExpenses = null;
MiscellaneousExpenses updatedMiscellaneousExpenses = null;
EmployeeId updatedEmployeeId = expensesToEdit.getEmployeeId();
try {
updatedExpensesAmount = ParserUtil.parseExpensesAmount(modifyExpensesAmount(expensesToEdit,
editExpensesDescriptor));
updatedTravelExpenses = ParserUtil.parseTravelExpenses(modifyTravelExpenses(expensesToEdit,
editExpensesDescriptor));
updatedMedicalExpenses = ParserUtil.parseMedicalExpenses(modifyMedicalExpenses(expensesToEdit,
editExpensesDescriptor));
updatedMiscellaneousExpenses = ParserUtil.parseMiscellaneousExpenses(modifyMiscellaneousExpenses(
expensesToEdit, editExpensesDescriptor));
} catch (ParseException pe) {
pe.printStackTrace();
}
return new Expenses(updatedEmployeeId, updatedExpensesAmount, updatedTravelExpenses, updatedMedicalExpenses,
updatedMiscellaneousExpenses);
}
-
Each method will edit their respective field from
expensesToEditwith the same field fromEditExpensesDescriptor. The method will set isNegativeLeftover to be true and set isOverLimit to be true if the values is below 0 or exceed max value.
Code snippet modifyExpensesAmount method, one of the methods being called
private String modifyExpensesAmount (Expenses expensesToEdit, EditExpensesDescriptor
editExpensesDescriptor) {
NumberFormat formatter = new DecimalFormat("#0.00");
String newExpensesAmount = expensesToEdit.getExpensesAmount().toString();
double updateExpensesAmount = Double.parseDouble(newExpensesAmount);
String change = editExpensesDescriptor.getExpensesAmount().toString().replaceAll("[^0-9.-]",
"");
updateExpensesAmount += Double.parseDouble(change);
if (updateExpensesAmount < 0) {
setIsNegativeLeftover(true);
} else if (updateExpensesAmount > MAX_TOTAL_EXPENSES) {
setIsOverLimit(true);
} else if (updateExpensesAmount >= 0) {
newExpensesAmount = String.valueOf(formatter.format(updateExpensesAmount));
}
return newExpensesAmount;
}
-
The
AddExpensesCommandwill check for isNegativeLeftover and isOverLimit. If both are false, theAddExpensesCommandwill then update the updated expenses into CHRS.
Code snippet of isNegativeLeftover and isOverLimit check
if (getIsNegativeLeftover()) {
throw new CommandException(MESSAGE_NEGATIVE_LEFTOVER);
} else if (getIsOverLimit()) {
throw new CommandException(MESSAGE_VALUE_OVER_LIMIT);
} else if (!getIsNegativeLeftover() && !getIsOverLimit()) {
messageToShow = MESSAGE_SUCCESS;
model.updateExpenses(expensesToEdit, editedExpenses);
model.commitExpensesList();
}
model.updateFilteredExpensesList(PREDICATE_SHOW_ALL_EXPENSES);
}
The sequence diagram below illustrates the operation of the AddExpensesCommand:
Design Considerations
Aspect implementation of AddExpenses command:
-
Alternative 1 (current choice): Have a single command to add and modify expenses.
-
Pros: User isn’t require to ensure that there exist an expenses for the employee before modifying the expenses
-
Cons: Have to handle more conditions and possibly more prone to bugs.
-
-
Alternative 2: Have separate commands to add or modify expenses.
-
Pros: User won’t have the possibly of mixing up if they want to add new expenses or modify old expenses.
-
Cons: Requires user to know more commands.
-
Aspect: Expenses List Storage
-
Alternative 1 (current choice): Have expenses stored in XML format.
-
Pros: Easier to read, code and test XML files.
-
Cons: Can’t compute or tabulate data from file.
-
-
Alternative 2: Have schedules stored in CSV format.
-
Pros: More efficient way of storing tabular data like expenses
-
Cons: Harder to read as compared to XML file.
-
Expenses Report: expensesReport [Upcoming in 2.0]
Generates an expenses report with the total expenses amount claimed and have not been claimed for a year [Upcoming in 2.0]