PROJECT: MyMorise


Overview

MYMorise — Manage Your Money(MYM) is a desktop application for those who prefer tracking personal expenses on their computers. With MYMorise, you can easily see your daily expenses as well as a summary, along with other useful functionalities. More importantly, MYMorise is optimized for those who prefer to work with Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI) created with JavaFX. It is written in Java, and has about 20 kLoC.

Summary of contributions

  • Major enhancement: added the ability for expenses in foreign currencies to be stored and converted

    • What it does: when an expense is added to MyMorise, the system can automatically convert the amount specified in the expense relative to the currency that was specified in the expense.

    • Justification: This feature improves user experience significantly for managing expenses as the user no longer needs to convert a foreign currency manually back into their home(base) currency.

    • Highlights: This enhancement affects 80% of all functionality of the application as all amounts added to the system may need to be converted to ensure correct computations. This required careful planning as as soon as it is implemented, I anticipated a large number of regressions from existing implementations as well as regressions as the other components of the app are implemented. The exchange rate for converting the expense’s amount from its base amount to the specified currency is retrieved from the exchange data and stored together with the Expense. The reason for this is to ensure that an expense retains its converted value based on the rate at the time it was added, regardless of when the computation is being done. As currecy exchange rates are non-constant data, there is a small dependence on retrieving more updated data by making an API call in the form of a HTTP GET request to a publicly available endpoint. To ensure MyMorise remains responsive while the call is made, the HTTP Request is executed asynchronously with a strict 5 second timeout (a successful request should not take longer than 5 seconds). The fallback in poor/lack of internet conditions is a pre-loaded exchange rate stored in JSON. A successful response stores the exchange rate to the file system for the next run to use if there is no internet connection in subsequent runs, which mitigates conversion-error margins slightly.

    • Credits: The idea to add it came from a "if I was using this app" brainstorming session, however I must credit Budgeting/Finance Apps I had downloaded on Android that I played around, specifically WallyNext

  • Minor enhancement: added support for storing currencies.

  • Code contributed: [Functional code] [Test code]

PRs

Contributions to the User Guide

Initial format and structure of user guide together with all discussed User Stories

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.

Model component

ModelClassDiagram
Figure 1. Structure of the Model Component

API : Model.java

The Model,

  • stores a UserPref object that represents the user’s preferences.

  • stores a BudgetList object that represents the user’s budgets.

  • stores an ExpenseList object that represents the user’s untagged expenses.

  • stores an ExchangeData object that represents the foreign exchange rates for conversion of currencies.

  • exposes an unmodifiable ObservableList<Expense> and an unmodifiable ObservableList<Budget> that can be 'observed' e.g. the UI can be bound to either list so that the UI automatically updates when the data in the list change.

Storage component

StorageClassDiagram
Figure 2. Structure of the Storage Component

API : Storage.java

The Storage component,

  • can save UserPref objects in json format and read it back.

  • can save Budget objects in json format and read it back.

  • can save Expense objects in json format and read it back.

  • can read Exchange objects in json format.

Currency Conversion

Implementation

The Currency conversion is achieved by having a default base currency that all expenses and budgets use if one is not specified. The expenses that are stored with a different currency can be converted from the base currency and back. Every time a currency is specified, the present day exchange rate is stored with the expense. This is to ensure that the expense value is timeless.

This was implemented in the java.seedu.address.model.exchangedata package, which contains ExchangeDataSingleton, ExchangeData, and Rates. These classes facilitate retrieval of exchange rates that will be requested from various parts of the app, namely Expense and Budget.

Step 1 The foreign currency exchange rates are downloaded for the app to use from ExchangeRatesAPI.io using their endpoint https://api.exchangeratesapi.io/latest?base=SGD. The endpoint returns the data in JSON format, which works well with the existing JSON based storage used for Expense and Budget. This was implemented in the java.seedu.address.commons.utils package containing HttpsClientUtil which facilitates the Asynchronous calls to the endpoint to update the local copy of the foreign currency exchange rates upon app startup.

Step 2 The JSON response is persisted on disk. A default data-set of exchange rates will be generated at runtime (but not stored) in the case of unstable or no internet access if one is not yet present. If one was present however a more recent one could not be downloaded, the existing data will be reused.

Step 3 If the exchange data is present as a JSON, it will first be loaded into its JSON reflection class JsonAdaptedExchangeData which depends on JsonAdaptedRates. These classes assist in the conversions of the JSON to the Model Class ExchangeData.

Presently, ExchangeData is also stored in the StorageManager which was intended to be used to store multiple instances of ExchangeData to load historical exchange rates when the user does an edit, however this proved to be infeasible to implement within the course of the project.

Step 4 The ExchangeDataSingleton is updated with the new ExchangeData using the ExchangeDataSingleton#updateInstance. The reason for using the Singleton pattern in this case was to ensure that one and only one instance of ExchangeData is being referenced to retrieve data at any point in time. Since it is also required to be accessed by Expense and Budget.

Step 5 Conversions are done in Expense#getConvertedAmount for conversion back into the base currency (SGD) and Expense#getConvertedAmount(Currency currency) for conversions to any other currency. As most transactions in the app that require the conversion of currency involve an expense (including recomputing amountLeft of Budget), the computation is done in the expense. The following are the instance where the currency is converted:

  • Whenever an expense is displayed on the ExpenseCard or CommandResult, its converted value will be computed and displayed together with its conversion rate and original amount.

The following sequence diagram shows how currency conversion works:

CurrencyConversionSequenceDiagram

Design Consideration

There were 3 main design choices we had to choose from for the implementation of the Currency Conversion Feature.

  • Alternative 1: Do not store currency rates when expenses are added with a selected currency and simply store the currency.

    • Pros: Easy to implement, recompute the amount of the expense based on the present day currency exchange rate.

    • Cons: If there was a crash in any currency, the expense amount will no longer be timeless. For instance, if an expense of of 10 MYR was stored today and was converted to 3 SGD (converted at 3.33), then the exchange rate became 2.0 the following day, the budget would reflect the amount left wrongly when it was recomputed the next day.

  • Alternative 2: Persist all the exchange rates to file every time the app is launched to ensure that the user’s expenses are computed based on the exchange rate that was on that specific day. The specific exchange rate used is not stored in the expense itself.

    • Pros: Achieve timeless expense which will provide the most accurate values when computing the budget amount left.

    • Cons: Slightly harder to implement as there are multiple sources for exchange rate data based on dates. This will also bloat the amount of space requirement of the app as time persists.

  • Alternative 3: Store the specific exchange rate used together with the expense whenever it is added, update the exchange rates daily and only persist the latest rates.

    • Pros: Achieve timeless expense which will provide the most accurate values when computing the budget amount left, without the need to store historical exchange rates. Reduced space used and single source of truth for computing exchange rates.

    • Cons: Expenses added in the past that have their currencies changed will not be able to use the exchange rate of that specific day.

Alternative 3 was chosen as it was a balance between the cons across all 3 considerations, and the timelessness of an expense carries more weight than the unlikely event that the user would need to alter the currency of a past expense.