Overview
For our project for our Software Engineering course, my team comprising 5 students (including myself) was given the codebase for a command line desktop application called Address Book, and we were required to morph this given codebase into an application of our choice. We chose to morph the given application into a group expense management app called SplitWiser. This enhanced application enables people to simplify expense and debt tracking in group activities, so that people will know exactly how much to pay each other at the end of the activity.
I was responsible for writing the expense adding feature. This makes my role particularly crucial as the expense
command is most likely the command that a user interacts with the most. The following sections describe my contributions in greater depth, in addition to the documentation that I also added pertaining to the expense feature.
Summary of contributions
-
Major enhancement: Added the ability to add expenses
-
What it does: The
expense
command allows the user to add expenses to an activity by first specifying who paid and how much, followed by an optional description of the expense, then optionally listing people who were involved in the expense. -
Justification: This is important as it gives users the necessary precise control over the people required to pay in each expense. In a real-world group activity scenario, it might not be the case that everyone is involved in every part of the activity that involves money. For example, one might go out for a meal with friends but opt out of buying dessert later. Having such flexibility enables the user to manage a wide range of real-world scenarios.
-
Highlights: The design of the
expense
command was particularly tricky due to the limitations of a CLI interface. The tough part was in trying to strike a balance between keeping the command simple enough to use yet provide enough flexibility for the user to specify various possible combinations of expense details. We went through various possible designs for theexpense
command and took the user experience into consideration for each case, before finally settling on the way theexpense
command is currently structured.
-
-
Minor enhancement: Updated the
edit
command to only work when viewing a contact or activity-
What it does: The
edit
command allows the user to modify certain fields of a contact or activity when viewing them. The original Address Book application already had theedit
command built in, but its behaviour was modified to suit SplitWiser. -
Justification: In the original Address Book application, the user was allowed to edit various fields by specifying the index of an item in the visible list, then specifying the respective fields to edit and update. However, this structure is not ideal anymore as our list user interface was updated to not show every field, meaning that the user would not be able to see what exactly was being edited. Thus, the decision was made to enable
edit
only when viewing an item as this reduces the risk of the user making unintentional edits.
-
-
Minor enhancement: Added a backend
Context
class that allows for various application contexts and contextual behaviour for various commands-
What it does: The
Context
class keeps track of the current application context that the user is in. ThisContext
object gets updated upon the execution of each command where necessary.
-
-
Code contributed: Link to tP dashboard
-
Other contributions:
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 to an activity : expense
This command creates a new expense with a list of contacts, an amount and an optional description, and adds it to the currently viewed activity. The first contact in the list is taken to be the person who paid for the expense, and the remaining people will be counted as owing the first person money. Any duplicated person will be ignored.
If only one contact is specified in the list, then SplitWiser will assume that all current participants in the activity are involved in this expense and thus owe this person money.
If no activity is being viewed, the description is compulsory - a new activity will instead be created with the same title as the description (as if activity t/ACTIVITY_NAME
was called). The expense and contact(s) will then be added to the activity. You may wish to change the title of this new activity using the edit
command thereafter (refer to Edit an existing contact or activity : edit
).
Format: expense p/PERSON e/AMOUNT_PAID [p/PERSON …] [d/DESCRIPTION]
Examples:
-
expense p/David Lim e/100
Adds a single expense of $100 by David Lim to the currently viewed activity. If David is not in the current activity, an error will occur and no expense will be created. Otherwise, all existing participants of the activity will now owe David a portion of the $100 (left image).
If no activity is currently viewed, an error will occur as there is no description provided to use for automatic activity creation to contain this expense (right image):
-
expense p/David e/100 p/Bernice p/Irfan d/Drinks
Adds an expense of $100 to the currently viewed activity by David where Bernice and Irfan are involved i.e. Bernice and Irfan owe David a portion of the $100. This expense will be namedDrinks
(left image). If any one of the participants are not in the present activity, then an error will occur and no expense will be created.
Alternatively, if no activity is currently viewed, an activity titledDrinks
will be created to contain this expense. David, Bernice and Irfan will then be added to the activity (right image):
It is in fact possible to add an expense where no one else is involved in by specifying that the people involved only include the person who is paying, e.g. expense p/John Doe e/15 p/John Doe d/Cab This adds an expense of $15 by John Doe called Cab where only John himself is involved, thus no one owes anyone anything. This can be used to add an expense simply for recording purposes if desired, though it is not an intended use case for SplitWiser since there is no debt to calculate and resolve.
|
Edit an existing contact or activity : edit
This command edits some details of the current contact or activity in view. Note that contact names must be unique, so if a specified name already exists in the address book, the edit will not be processed.
Format: edit [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [tag/TAG …]
for contacts OR edit [t/ACTIVITY_TITLE]
for activities.
Examples:
-
edit p/999
Edits the phone number of the current contact in view to999
. No changes are made if a contact is not being viewed.
-
edit t/Lunch
Edits the title of the current activity in view toLunch
. No changes are made if an activity is not being viewed.
Add variable split expenses to an activity: varexpense
(v2.0 only)
Creates a new expense with a list of (contact, amount) pairs and an optional description, and adds it to the currently viewed activity. The very first (contact, amount) pair will represent the person paying and the amount paid. Every subsequent (contact, amount) pair will then represent the cost incurred by each person in this expense.
Format: varexpense p/PERSON e/AMOUNT [d/DESCRIPTION] [(p/PERSON e/AMOUNT) …]
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. |
Expense class
Expense
is a class used to record expense information. It contains the following information:
-
Paying person
Each expense must be paid by someone, which is recorded as personId
internally. As the name suggests, only the primary key of the Person
paying is stored.
-
Other involved people
Each expense also involves some people who will be taken to owe money to the paying person. This is recorded as involvedIds[]
internally, which only stores the primary key of each involved person.
-
Amount
The amount of money spent. The value of an Amount
is stored as a double
, and must be non-negative. Refer to Limitations of double
for a more in depth analysis the numerical errors that may arise due to the limitations of this representation.
-
Description
The description of an expense. The description may be blank if the user chooses to not describe what it is about.
-
Deleted flag
This flag tracks whether an expense has been marked as deleted or not, and is stored as a boolean value isDeleted
. For accountability purposes, we do not allow an Expense
to be deleted from an Activity
entirely, and instead use this flag to keep track of it. Expenses can only be fully deleted by deleting the entire activity.
-
Settlement flag
SplitWiser supports 2 main types of "expenses": one is an actual expense, and the other is a transaction between people to pay off debts. The latter is what we call a settlement, and is indicated by the boolean isSettlement
.
The following diagram describes the structure of the Expense
class:
Design considerations
Aspect: How to actually represent an Expense
-
Alternative 1: An expense is represented by a paying person and an amount. Everyone in the activity is then taken to owe the paying person an equal portion of the paid amount.
-
Pros: Extremely simple to use.
-
Cons: Overly idealised and simplified and assumes that everyone is involved in every expense. This severely hinders its utility in real scenarios as this is unable to account for even simple deviations in debt patterns.
-
-
Alternative 2 (current choice): An expense is represented by a paying person, amount, and a list of people involved. Everyone who is specified to be involved then owes an equal portion of the paid amount to the paying person.
-
Pros: Reasonable flexibility as it enables a wide range of possible expense sharing combinations.
-
Cons: Forces equal split among participants in the involved list, which is still going to result in limited utility in some real scenarios. A simple example would be that a group of people can have a meal together but each person’s meal would cost a different amount.
-
Reason for choice: Although the above con can be a very real issue from a user’s standpoint, we believe this is the best balance between flexibility and usability.
-
-
Alternative 3: An expense is represented by a paying person, an amount, a list of paying people and the amount each person should pay back the paying person.
-
Pros: Ultimate flexibility and addresses the con above.
-
Cons: Significantly complicates the backend management of expenses. While this is something that will definitely be addressed in future versions of SplitWiser (e.g. adding a new
varexpense
command that enables this), we opted for a simpler version ofExpense
due to time constraints. The areas that need to be taken into consideration include (but may not limited to):-
Design of the command for the user in a way that is intuitive
-
Managing the event that the amount list does not add up to the amount paid
-
GUI representation of the more generalised version of
Expense
-
-
Create expense feature
The mechanism to create expenses is facilitated by Expense
. Each Activity
stores a list of Expense
called expenses
, representing the expenses incurred in the course of this activity.
When creating an expense, an amount and at least one person (the paying person) must be specified, and information about an expense cannot be modified once it is created. The only exception to this would be to delete an expense, but expenses also cannot be un-deleted once marked as deleted.
In terms of the name searching logic to actually identify who is involved, it is identical to that which is used in the creation of activities (see [Create activity feature]), but with a few key differences:
-
The expense will only be created if all sets of keywords result in a unique match. For instance, if the user specifies
p/John Doe p/Mary p/James
, but only the keywordJames
does not successfully identify a unique person, then the entire expense will not be created. -
The search scope is contextual. If creating an expense outside an activity context, it will search for matches in the entire Address Book. However, in an activity context, it will only search for matches among existing activity participants.
Here, it is important to realise that duplicating the name searching logic ensures a consistent user experience, as the user has no reason to expect that the search behaviour would be different. For example, if john
was successfully used to identify a contact named John Doe
when creating an activity, the user has no reason to expect that john
would not be able to identify John Doe
for an expense.
Here is a sample use case in creating an expense:
Step 1. The user launches the application for the first time. The SplitWiser
will be initialized with the initial address book and activity book state.
Step 2. The user executes activity t/breakfast p/Jo p/Bob p/Alice
command to create an activity named breakfast with 3 participants added to the activity. The context is then switched to this new activity.
Step 3. The user executes expense p/Jo e/10
to indicate that Jo paid $10 for something (unnamed as description is not specified) that is shared by everyone i.e. Bob and Alice. This happens because the default behaviour for expense
is to assume everyone is involved if no one else is specified explicitly. expense
command calls Activity#addExpense()
and adds the new expense into the activity. The debt algorithm will then instantly recompute the debt matrix (see [Debt simplification algorithm]) for the activity, and store the expense object inside the activity’s expense list (i.e. expenses
).
The following sequence diagram shows how the expense adding operation works:
The lifeline for ExpenseCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
The following activity diagram encapsulates the high level logic of the expense adding operation:
Appendix A: Limitations of double
It is important to note that SplitWiser in its current iteration uses double
to represent expense amounts and debt values. While we maintain that this choice is sufficient for the intended uses of SplitWiser (i.e. to simplify debt calculations among friends), it is nonetheless essential as the developer to be aware of the issues that can arise with using such a representation.
Java uses the double precision 64-bit IEEE 754 floating point format to represent doubles, which internally comprises 1 sign bit, 11 exponent bits and 52 mantissa bits. The finite digit nature of the mantissa consequently results in potential loss of precision when attempting to add or subtract numbers which have a wide range in magnitude. One example is as follows:
Here, if Alex attempts to settle the debt with David using settle p/alex p/david
, the debt re-calculations will result the debt still being non-zero. In fact, if the user attempts executes another settle p/alex p/david
, the debt still remains!