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:
AddExpenses
Command andDeleteExpenses
Command-
What it does: The
AddExpenses
command allows user to create an expenses or modify an expenses that already expenses.DeleteExpenses
command 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
SelectExpenses
Command to allow user to select any expenses in expenses list -
Added
ClearExpenses
Command 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
addExpenses
feature alongside model component and instructions for manual testing (Pull requests: #102, #232, #270) -
Updated User Guide to ensure
addExpenses
,deleteExpenses
,SelectExpenses
andclearExpenses
commands 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
addExpenses
allows 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:
-
isValidEmployeeId
fromEmployeeId
class -
isValidExpensesAmount
fromExpensesAmount
class -
isValidTravelExpenses
fromTravelExpenses
class -
isValidMedicalExpenses
fromMedicalExpenses
class -
isValidMiscellaneousExpenses
fromMiscellaneousExpenses
class
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
AddExpensesCommand
will 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
AddExpensesCommand
will 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
AddExpensesCommand
will create anexpensesToEdit
object TheAddExpensesCommand
will then callcreateEditedExpenses
method withexpensesToEdit
andEditExpensesDescriptor
object 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
createEditedExpenses
method will callmodifyExpensesAmount
,modifyTravelExpenses
,modifyMedicalExpenses
,modifyMiscellaneousExpenses
, methods withexpensesToEdit
andEditExpensesDescriptor
object 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
expensesToEdit
with 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
AddExpensesCommand
will check for isNegativeLeftover and isOverLimit. If both are false, theAddExpensesCommand
will 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]