Overview
Budget Buddy is a desktop application that allows users to track their expenses. The user interacts with it using a Command-Line Interface (CLI), and it has a Graphical User Interface (GUI) created with JavaFX. It is written in Java, and has about 20 kLoC.
About the Project
Budget Buddy has a number of features to help users track their expenses and deeply customize their experience while using the app.
As an expense tracker, Budget Buddy provides the ability for users to record and categorize transactions they have made. Transactions can also be divided into user-created accounts, helping users to isolate transactions for specific events or holidays. Budget Buddy further allows the user to track loans that they owe others or are owed to them, helping them remember to pay back or reclaim borrowed money. Unbalanced group payments can also be split equally by Budget Buddy, and the resulting debts the user owes or is owed can be recorded as loans automatically.
Budget Buddy also provides features to deeply customize the user experience. Users can create rules to automate many actions; for example, they could create a rule that adds a transaction to a specific category if said transaction exceeds a certain amount. As the target audience comprises of computing students, Budget Buddy also allows users to utilize their computing know-how to write custom scripts to manipulate the app. For example, users can write scripts to add huge numbers of transactions, or scripts to trigger alarms when their expenses have exceeded a certain limit.
My role in the project was to implement all facets regarding the loans feature. Chiefly, I added the ability for the user to add, edit, view and delete loans. Users can also mark loans as paid or unpaid, and when viewing their list of loans they can sort or filter the list by amount, date and more. I was also responsible for implementing the complex algorithm necessary to split and divide an initially unbalanced group payment; the main challenge in this scenario was to devise an algorithm that worked for any group size and every variation of initial unbalance.
Aside from the implementation of features, I also aided in the delegation of roles among the team, upholding the quality of project documentation, and ensuring the team remained on task.
Summary of contributions
-
Major enhancement: Added the ability to track loans and split unbalanced group payments equally
-
What they do: Tracking loans allows the user to track all the money that they owe/are owed to other people. Splitting group payments through the app allows the user in avoiding messy miscalculations and over/under-paying their friends.
-
Justification: This feature improves the product significantly, as both lending/borrowing money and paying for large items as a group are common occurrences that affect one’s expenses, especially for students.
-
Highlights: Implementing loan-tracking required an in-depth analysis of design alternatives, especially regarding the structure of the loan-person relationship. Building it on top of and mutating the original brownfield project also proved challenging. Splitting group payments proved difficult due to the need for an accurate yet efficient algorithm. Great care had to be taken to correctly implement each of the many steps in the complex final result.
-
-
Code contributed: [RepoSense Report]
-
Other contributions:
-
Documentation:
-
Community:
-
Contributions to the User Guide
Given below are a few sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. Due to limited space, not all my contributions could be included; please refer to the full User Guide to read them in their entirety. I was responsible for the section on Managing Loans. |
Managing loans: loan
Budget Buddy comes with the ability for you to keep track of who owes you money and exactly how much they owe you. Never again allow those small sums you lend out to accumulate into a massive pile of forgotten gold. You can also record who you owe money to, saving you the embarrassment of being publicly reminded by your loan shark friends. Finally, it can help you calculate who owes who how much when you and said friends wake in the morning after an expensive night out.
Your loans can be viewed in the Loan tab, as seen in the figure below:
Mark loan(s) as paid: loan paid
This command marks one or more loans in the list as paid. A paid loan can be visually distinguished by a large "tick" icon to the left of its index:
Multiple loans can be marked at once. This can be done by specifying several indices or at least one person to target. The format of the command is as follows.
Format: loan paid [<index …>] [<p/person …>]
Examples:
-
loan list
loan paid 1 2 3
Lists all loans, then marks the first three loans in the list as paid. -
loan paid p/John p/Adam
Marks all loans pertaining to John and Adam as paid.
Calculate loans: loan split
This command takes a group of persons and a list of amounts each person has paid, before calculating which persons need to pay which other persons such that the total amount paid is split equally among all the group’s members.
For example, let’s say you’re out for dinner with two friends.
When the hundred-dollar bill arrives, you pay for the first sixty and one of your two friends pays for the remaining forty.
The loan split
command can now help you to calculate how much your two friends owe you,
such that the hundred-dollar bill is split equally among you three.
This command comes with many optional arguments, all of which are explained below. The format of the command is as follows.
Format: loan split p/<person> x/<amount paid> [max/<limit>] … [me/<your name> [w/<date>] [d/<description>]]
To switch between your loan list and split list, press Ctrl+D (or Cmd+D for Mac) while on the Loan tab.
The split list will initially be empty, but once a list is calculated it will persist in the Loan tab across sessions (until a new calculation is made). |
Examples:
-
loan split p/John x/0 p/Mary x/40 p/Peter x/60
Calculates the money owed betweenJohn
,Mary
andPeter
for a scenario where (initially)Mary
paid40
andPeter
paid60
. The resulting display should show thatJohn
owesMary
$6.66 and also owesPeter
$26.67, as seen in the image below.
-
loan split p/John x/0 max/10 p/Mary x/10 p/Peter x/90 me/Mary d/Dinner.
In this scenario,Mary
paid10
andPeter
paid90
.max/10
restricts the final amountJohn
pays/owes to10
, despite the total amount being0 + 10 + 90 = 100
. Furthermore,me/Mary
marksMary
as the user (you), so any debtsMary
owes/is owed will be added to the normal loan list with the descriptionDinner.
. The resulting display should show thatJohn
owesPeter
$10 andYou
owePeter
$35, as seen below.
The latter debt will also have been added to your loan list,
which can be checked using the command loan list
or by pressing the hotkey mentioned above.
Contributions to the Developer Guide
Given below are a few 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. Due to limited space, not all my contributions could be included; please refer to the full Developer Guide to read them in their entirety. I was responsible for the section detailing the Loans Feature. |
Loans Feature
Implementation
The Loans feature exists outside of the Account/Transaction mechanisms.
It adds a separate LoansManager
alongside the main AccountsManager
, with Loan
objects stored internally in an internalList
.
The following class diagram demonstrates the association between the LoansManager
and Loan
objects.
Miscellaneous methods (such as LoansManager#getLoans
and LoansManager#getLoansCount
) are omitted.
Given below is an example usage scenario and how the LoansManager
behaves at each step.
Step 1.
The user launches the application.
If loans.json
exists on the hard disk, its data is loaded into internalList
.
Otherwise, loans.json
is created and the LoansManager
initializes with an internalList
containing a few sample loans.
Step 2.
The user executes the command loan out p/John x/4.20 d/Paid for his lunch
to add a new loan.
This creates a new loan toAdd
of amount
4.20 out
to the person
John, with the description Paid for his lunch
.
Since the user did not provide a date, the current system date is used for the date
of toAdd
.
LoansManager#addLoan(Loan toAdd)
is called and
(after verifying that toAdd
does not already exist in internalList
)
toAdd
is added to internalList
.
The following sequence diagram illustrates the process of adding a loan:
In general, the rest of the operations work using a similar sequence of steps.
Some commands might create a new Loan
object (as shown above) while others might just use the Index
of a loan (e.g. loan delete
).
Multi-Loan Targeting
The user can target and act on multiple loans with a single command.
For example, loan paid 1 3 4
can be used to mark the first, third and fourth loans in internalList
as PAID
.
Alternatively or additionally, loan paid p/John p/Mary
can also be used
to mark all the loans of the persons John and Mary in internalList
.
The LoansManager
handles this by executing the appropriate operation repeatedly.
In the case of loan paid 1 3 4
, LoansManager#editLoan
is called once for each of the three loans.
To account for the fact that the list size might change after each operation, the size of the list before and after each operation is compared. If it has changed, the targeted indices are adjusted accordingly.
If any of the target loan indices cannot be found by the LoansManager
in internalList
,
they are added to a missingLoanIndices
list.
Similarly, target persons that cannot be found are added to a missingPersons
list.
Both lists are displayed to the user after other target loans that are in internalList
have been acted upon,
notifying the user that the target indices
/persons
could not be found.
Multi-loan targeting is implemented for the commands loan paid
, loan unpaid
and loan delete
.
Loan Splitting
Loan splitting exists as a command that the user can execute. Its purpose is to split a large, initially unbalanced group payment equally among the group’s members. The following example scenario should clarify the purpose of the command:
John, Mary and Peter go out for dinner. The meal costs $100, so Peter pays $90 and Mary covers the remaining $10. However, the three want to split the bill equally among themselves.
John executes the command loan split p/John p/Mary p/Peter x/0 x/10 x/90
.
Budget Buddy then outputs a list of the necessary payments between the three.
From this list, John can now see that he owes Peter $33.33 and that Mary owes Peter $23.33.
Given below is an example scenario to demonstrate how the final list is calculated. The algorithm as a whole can be summarized in the activity diagram below, which might prove helpful for following the steps in the example scenario:
Step 1.
The user executes the command loan split p/Ben p/Duke p/Adam p/Zed x/0 x/20 x/80 x/50 max/10 me/Ben d/Dinner
.
In this scenario, out of the total bill of $150,
Ben
has paid $0,
Duke
has paid $20,
Adam
has paid $80
and Zed
has paid $50.
Furthermore, max/10
specifies that Ben
should only pay/owe up to $10 overall.
Finally, me/Ben
marks Ben
as the user;
all debts involving Ben
should be added to the normal loan list with the description Dinner
.
Step 2.
LoanSplitCommandParser
parses the persons, amounts, and max shares into List<Person>
, List<Amount>
and List<Long>
respectively.
me/Ben
and d/Dinner
are parsed into Optional<Person>
and Optional<Description>
.
A new LoanSplitCommand
is instantiated with the lists and optional objects.
Step 3.
LoanSplitCommand
replaces Ben
in List<Person>
with a Person
with the Name
You
.
Using the static methods of a LoanSplitCalculator
class, it then begins executing the following algorithm:
-
The
defaultSharePerPerson
is calculated. In this scenario, $150 should be split among the 4 persons. However, asBen
has a max share of $10, the other 3 persons must divide $140 among themselves, resulting in adefaultSharePerPerson
of $46.66. -
Each person in
List<Person>
is given abalance
, calculated using the amount they paid initially (fromList<Amount>
) minus either thedefaultSharePerPerson
or their max share if present. -
A list of all possible sub-groups (combinations) of persons is generated. In this scenario, the number of sub-groups for the 4 persons would be 16.
-
For each sub-group, if the sum of their balances is zero, then the following steps are performed:
-
Take the persons with the smallest and biggest balances: the
debtor
andcreditor
respectively. -
Transfer money between the two such that one or both of their balances reaches zero. The person(s) with a balance of zero are then removed from the group, and a record of the
debtor
,creditor
andamountTransferred
is stored in aList<DebtorCreditorAmount>
. -
Repeat until the sub-group contains less than two persons.
-
-
After every sub-group has been processed,
List<DebtorCreditorAmount>
is used to create the finalList<Debtor>
stored inLoansManager
.
Step 4.
All debts involving the person You
in List<Person>
are used to create loans with the description Dinner
.
These loans are added to internalList
in LoansManager
using LoansManager#addLoan
.
Step 5. The list of debtors
in LoansManager
is displayed to the user.
In this scenario, the display will show that
Duke
owes Adam
$26.66
and You
(Ben
, the user) owe Zed
and Adam
$3.32 and $6.68 respectively.