PROJECT: Coupon Stash


Ui

Overview

Coupon Stash is a desktop application for keeping track of your coupons, coupon usage, and coupon savings. Coupon Stash is written in Java, JavaFX and FXML, by a team of 5 committed individuals (including me). Featuring a command-line interface as well as UI elements like calendars and graphs, Coupon Stash is an amazing application and a prominent highlight of my abilities.

Summary of contributions

In this section, I will provide a brief summary of my contributions to Coupon Stash, as well as to documents like the User Guide and Developer Guide.

  • Major enhancement: The savings feature allowing users to keep track of how much they have saved from using coupons

    • Tracking savings per usage allows the user see how much they have saved, over the course of a certain time period.

    • This feature improves the product significantly because a key part of coupons is the savings and benefits it brings to the user, and so there is demand for knowing how much benefits you have reaped easily.

    • This feature was not easy to add as it required consideration about the various forms of savings that coupons may offer, such as a percent off the original price, a flat monetary amount, or even free gifts. Also, it required integration with other features, like the "used" feature which allows users to mark their coupons whenever they use them.

  • Minor enhancement: added a "setcurrency" command so users can customise the application to different regions, which would be helpful for exchange students for example.

  • Code contributed: View alcen’s contributions with RepoSense here

  • Other contributions:

    • Project management:

      • Managed releases v1.2.5 - v1.3.1 (3 releases) on GitHub

    • Enhancements to existing features:

      • Updated the GUI with a savings graph and tab (Pull request #212)

      • Wrote additional tests for savings features to increase coverage (Pull requests #327)

    • Tools:

      • Integrated Github plugins (Travis CI, AppVeyor) to the team repo

Contributions to the User Guide

Here are some sections of the User Guide authored by me.

Viewing savings: saved

Shows you how much you have saved by using coupons in Coupon Stash. There are three ways to use this command:

  • If just the word "saved" is entered, the total savings accumulated since you started using Coupon Stash will be shown.

    Format: saved

  • If a specific date is given, Coupon Stash will show you savings earned only on that day.

    Format: saved d/DATE_TO_SHOW

  • If a start date and end date are given, Coupon Stash will show you the total savings accumulated over all the dates between that start date and end date, inclusive of those dates as well.

    Format: saved sd/START_DATE e/END_DATE

  • Dates are given in Coupon Stash date format, namely D-M-YYYY where D and M can be single or double digits.

  • Shows you a numeric value (e.g. 12.00 to represent twelve dollars/euros/pounds/pesos) that represents how much money you saved since a certain date, as well as certain items that you might have saved.

  • This value changes depending on which coupons were marked as used during the time period specified.

Examples:

  • saved d/1-3-2020
    A message with your total savings and saveables saved on 1 March 2020 will be displayed in the Command Result Box.

  • saved sd/1-5-2019 e/20-3-2020
    A message with your total savings and saveables saved between 1 May 2019 and 20 March 2020 will be displayed in the Command Result Box.

Setting currency symbol: setcurrency

Sets the currency symbol used in Coupon Stash. This will force Coupon Stash to reload all coupons to use the new symbol instead.

  • The currency symbol may affect the visual look of the program, as well as modify commands entered!

  • For example, adding a coupon with savings of a certain monetary amount will require you to use the new symbol instead.

Format: setcurrency ms/NEW_MONEY_SYMBOL

Examples:

  • setcurrency ms/€
    Sets the money symbol used in commands, as well as to display coupons' savings, to € (Euro). The changes will be visible immediately.

Contributions to the Developer Guide

These are some sections I contributed to the Developer Guide. Highlights include frequent use of Unified Modelling Language (UML) diagrams to illustrate associations between program components.

Savings per use and total amount saved

To allow users to keep track of how much they have saved (after all, the whole point of coupons is to offer certain tangible benefits, encouraging purchases by customers), Coupon Stash automatically tracks the user’s savings as they use their coupons that are handled in the application.

To achieve this, Coupons have to store two different fields:
1. Amount of savings each use of a coupon provides
2. Total amount of savings accumulated from using a certain coupon

Class structure of Savings

Just for reference, the image below shows the class diagram for the Savings class. It is compulsory for each Coupon to contain an Savings object, that represents what the user would gain from 1 use of that Coupon.

SavingsClassDiagram
Figure 1. Class diagram describing the structure of Savings.

A Savings object can hold a PercentageAmount, MonetaryAmount or Saveables, which represents discounts like "$5 off", "10% off" and "free door gift" respectively.

The table below shows which are valid Savings objects, and which are not.

Table 1. A list of different Savings objects, which could be valid or invalid.

Object Diagram

Comments

Savings with percentage amount.

s1valid

Valid

Savings with monetary amount.

s2valid

Valid

Savings with saveable items.

s3valid

Valid

Savings without any fields present.

s4invalid

Invalid: Savings must have at least one field

Savings with percentage amount and a saveable item.

s5valid

Valid: Savings can have both a percentage amount and saveables

Savings with monetary amount and multiple saveable items.

s6valid

Valid: Savings can hold more than one Saveable

Savings with monetary amount and percentage amount.

s7invalid

Invalid: Savings cannot have both a MonetaryAmount and PercentageAmount

As can be seen from the table, Savings cannot be completely empty, and Savings cannot have both a MonetaryAmount and PercentageAmount (it does not make much sense to have a voucher that says "10% and $5 off").

PureMonetarySavings and DateSavingsSumMap

In order to calculate the total amount saved, Coupons also store information about how much the user saves, and storage is done at the moment the user uses the coupon. This information is stored in the form of PureMonetarySavings, which is a subclass of Savings that never holds PercentageAmounts. The class diagram below illustrates this.

PureMonetarySavingsClassDiagram
Figure 2. Class diagram describing the structure of PureMonetarySavings.

The reason why PercentageAmounts are not allowed in accumulated savings is because a percentage discount is a relative value that depends on the original price of the product, and cannot be easily added up in a way that allows users to accurately measure how much they have saved from their coupons.

PureMonetarySavings are stored in a DateSavingsSumMap, which is a hash table that links the current date (LocalDate) to the savings earned (PureMonetarySavings) on that date. Each Coupon holds a DateSavingsSumMap. The next image shows the class diagram of the DateSavingsSumMap.

DateSavingsSumMapClassDiagram
Figure 3. Class diagram describing the structure of DateSavingsSumMap.

The following section describes the processes that follow whenever a user marks a Coupon as "used" with the used command.

Implementation of used command

When the user enters a used command, the actions taken by Coupon Stash change depending on whether the Coupon’s Savings stores a MonetaryAmount of PercentageAmount. The following activity diagram shows what happens when the user runs a used command.

UsedCommandActivityDiagram
Figure 4. Activity diagram showing the execution of a used command.

In terms of the implementation, the next two images shows the sequence diagram that models the successful execution of a used command within the actual program components.

More specifically, the used command executed is used 1 $100, and the state of the system is such that a Coupon with PercentageAmount in its Savings (no MonetaryAmount) and with Usage not at its Limit is located at index 1. Also, the money symbol set in the user preferences would be $, which makes this command a valid one that will execute successfully.

UsedCommandSeqDiagram
Figure 5. Sequence diagram showing how a UsedCommand is executed.

The money symbol set in the user preferences is retrieved by CouponStashParser, which passes it to UsedCommandParser that will use this symbol to parse the used command.

Also, within UsedCommand, the UsedCommand#execute() method will cause the creation of a new Coupon with the correct recorded number of uses and amount of savings earned. The next sequence diagram shows how a successful UsedCommand#execute() method produces the new total savings value for the new Coupon.

UsedExecutionSeqDiagram
Figure 6. Sequence diagram showing how UsedCommand updates the total savings.

In the end, the total savings value of the Coupon is updated. This total savings is represented by a DateSavingsSumMap.

One key implementation within the UsedCommand is the checks that it has to make to ensure the valid usage of a Coupon. Below is an activity diagram to show the flow of checks within the UsedCommand#execute() method.

Implementation of saved command

Now that we have seen how the used command works, we can look at how the saved command works. While used stores the amount of savings that the user has earned on a particular day, saved retrieves the amount of savings earned as recorded by Coupon Stash, given a particular time period.

The saved command works similarly to the used command, where a SavedCommandParser will be created by Logic to split up the raw String into its arguments, creating a SavedCommand. Let’s look at how a SavedCommand would be executed.

SavedExecutionSeqDiagram
Figure 7. Sequence diagram showing the execution of a SavedCommand.

Hence the SavedCommand loops through all Coupons to add up the savings earned from a particular time period, or from all dates if no time period is specified.

Design considerations

Based on the User Stories, there is a desire for tracking how much one has saved by using Coupon Stash, as well as for viewing total savings easily. Below are some alternative implementations of savings tracking and viewing that were considered by the developers, but were rejected in favour of the current implementation.

Alternatives:

  • Restrict each Savings to a concrete monetary value

This would make the implementation of Savings much simpler, as there would not be a need for separate classes like PercentageAmount, MonetaryAmount and Saveables. However, this might burden the user with calculating how much they would save in terms of dollars and cents, when many coupons and discounts come in the form of certain percentage reductions of the original price, as well as free gifts or benefits that cannot be translatable to a concrete monetary amount.

Hence, it was decided to rely on a few different representations of Savings that can be earned from using a Coupon, as well as a Savings class that could refer to any of these representations, or even a logical combination of these representations.

  • Each Coupon stores a MonetaryAmount, PercentageAmount and Saveables directly

This would eliminate the need for the intermediary Savings class and reduce complication in the program code slightly. But, it would be difficult to ensure that at least one such field exists in the Coupon, or guarantee that the Coupon would have one such field.

The Coupon class would have to hold the logic for determining whether it had a valid combination of MonetaryAmount, PercentageAmount and Saveables, which does violate Single Responsibility Principle as the Coupon class now has another reason to change (if we would want to allow both MonetaryAmount and PercentageAmount on a Coupon for instance).

Hence the Savings class was decided to handle this responsibility, as well as abstract away the implementation details of the multiple possible values and combinations of these values. This allows the Coupon to think in terms of an entire Savings object, rather than handle multiple different scenarios depending on which fields it has.