PROJECT: SplitWiser


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 the expense command and took the user experience into consideration for each case, before finally settling on the way the expense 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 the edit 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. This Context object gets updated upon the execution of each command where necessary.

  • Code contributed: Link to tP dashboard

  • Other contributions:

    • Documentation:

      • Did the initial revamp of our User Guide: #39

      • Updated some UML diagrams for the Developer Guide: #115, #198

    • 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 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]

  • Exactly one expense amount must be provided.

  • The allowable expense amount for any single expense must range from $0.01 to $1,000,000 when rounded to 2 decimal places.

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):

Expense1
  • 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 named Drinks (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 titled Drinks will be created to contain this expense. David, Bernice and Irfan will then be added to the activity (right image):

Expense2
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.

  • At least one of the optional fields must be provided.

  • Existing values will be updated to the input values.

  • Irrelevant parameters will be ignored. For example, edit p/999 t/new title when viewing an activity will only update the title to new title, and p/999 will be ignored.

  • Expenses cannot be edited.

Examples:

  • edit p/999
    Edits the phone number of the current contact in view to 999. No changes are made if a contact is not being viewed.

Edit1
  • edit t/Lunch
    Edits the title of the current activity in view to Lunch. No changes are made if an activity is not being viewed.

Edit2

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) …​]

  • The first amount (i.e. the paid amount) must be greater than or equal to the sum of all subsequent amounts specified.

  • If the numbers do not add up to the paid amount, any excess value is taken to be incurred by the paying person.

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:

ExpenseClassDiagram
Figure 1. 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 of Expense 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:

  1. 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 keyword James does not successfully identify a unique person, then the entire expense will not be created.

  2. 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:

ExpenseSequenceDiagram
Figure 2. Sequence diagram for adding an expense
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:

0
Figure 3. Activity diagram for adding an expense

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:

NumericalError
Figure 4. Example activity

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!