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:
-
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
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.
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, Coupon
s 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.
Savings
.A Savings object can hold a PercentageAmount
, MonetaryAmount
or
Saveable
s, 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.
Object Diagram |
Comments |
Savings with percentage amount.
|
Valid |
Savings with monetary amount.
|
Valid |
Savings with saveable items.
|
Valid |
Savings without any fields present.
|
Invalid: |
Savings with percentage amount and a saveable item.
|
Valid: |
Savings with monetary amount and multiple saveable items.
|
Valid: |
Savings with monetary amount and percentage amount.
|
Invalid: Savings cannot have both a
|
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, Coupon
s 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
PercentageAmount
s. The class diagram below illustrates this.
PureMonetarySavings
.The reason why PercentageAmount
s 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
.
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.
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.
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
.
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.
SavedCommand
.Hence the SavedCommand
loops through all Coupon
s 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
Saveable
s. 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 aMonetaryAmount
,PercentageAmount
andSaveables
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 Saveable
s,
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.