PROJECT: Seller Manager Lite Junior

Overview

Seller Manager Lite is a desktop application used for managing a business operational and logistical needs. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has >10 kLoC. It was written over the span of 2 months and morphed from the original Address-book 3 application.

Summary of contributions

  • Major enhancement: added ability to generate statistics

    • What it does: allows the user to query total Revenue, Cost and profit on all past orders. Contains 2 types of input mode:

      • No date input: This mode is to give users a quick way of obtaining the numbers

      • Date input: This mode is to produce a monthly chart view of the statistics

    • Justification: Being business owners, this feature should be very essential for the owners to be able to track their statistics and numbers while running their day to day operations. This allows them to more easily plan and monitor their business while using these numbers. By having this feature, the users of this app can review their performance of their products over the past few months, which is essential to business owners.

    • Highlights: This feature cut across several layers in the entire project e.g logic, UI, model and hence designing it in such a way that it was able to work with the existing Model View Controller architecture was a very important albeit tedious step. It required an in-depth analysis of design alternatives. This design choice is elaborated below in the developer guide writeup.

      • The implementation was also challenging as I made very sure no to violate any of the SOLID principles. At the same time, we were also still deciding how to implement the rest of the architecture for the other features. There was a very strong and conscientious effort to make the code easily extendable as there is a high possibility that this feature will have to scale exponentially.

      • A total of 4 attempts of refactoring was needed for me to reach the current version of the main feature.

    • Credits: The apache commons math libraries was used in the actual calculation. The main difficulty of this feature come from extracting the correct and appropiate data needed and in the appropiate manner.

  • Other Major enhancement:

    • My other contributions to the codebase is as follows:

      • Complete overhaul of User Interface. This includes generating the FXML files, creating the controller files, importing and GUI tests.

      • UI commands. This includes the command that is directly related to switching of UI e.g switch-o which allows the user to switch their their focused tab panel with a command.

  • Other contributions:

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.

Generate stats: generate-s

v1.4 only allows statistics on orders marked as Completed and has a Schedule.

This function calculates the three type of statistics as shown below.

All statistics that are calculated are only on orders in the archived panel that has been completed and with a valid schedule. Lacking any of this will cause its non-inclusion into the statistics calculation. The value calculated is the sum over the total time period within a month, meaning that it will take the date period, calculate the statistics for orders within that time period, split it by month and display it.

e.g Total revenue between 2019.11.12 and 2019.11.29 will calculate the order between these 2 dates, sum up the revenue (as it falls within a single month) and display this value.

  • Accepts two types of input:

    • Without date input - display total value for that particular statistics.

    • with date input - displays chart (Monthly value) and total value for that particular statistics.

Generates the statistics with no date input.

Used for calculating total profit, total revenue and total cost.
Format: generate-s s/stat

Type for stat includes: profit, revenue, cost
The argument must match these three words perfectly.

Shown below:

StatsExampleDefault

Generates the statistics with date input in chart format (in pop-up modal dialogue).
Format: generate-s s/stat d1/YYYY.MM.DD d2/YYYY.MM.DD
Example: generate-s s/revenue d1/2019.10.16 d2/2019.11.21
Format for the date is in YYYY.MM.DD e.g 2019.05.12

Shown below:

StatsExampleGraph

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.

  • My Contributions are:

    • Created the architecture, UI and Logic diagram (refer to DG portion with these diagrams)

    • Wrote the User Interface part of the DG (omitted limitations to user part due to length constraint of PPP)

    • Wrote the Statistic Feature in the DG


UiClassDiagram
Figure 1. Structure of the UI Component

API: Ui.java

The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, TabPanel, StatusBarFooter etc. TabPanel consists of CustomerListPanel, OrderListPanel, PhoneListPanel, CalendarListPanel and ArchivedOrderListPanel All these, including the MainWindow, inherit from the abstract UiPart class.

The UI component uses JavaFX UI framework. The layout of these UI parts is defined in matching .fxml files that are in the src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml.

The UI component,

  • Executes user commands using the Logic component.

  • Listens for changes to Model data so that the UI can be updated with the modified data.

  • Different changes in each tab or panel are controlled via the enum UiChange.


Statistics Calculation and Chart generation feature

Implementation

This statistic calculation and chart generation feature extends Seller Manager Lite which allows sellers to quickly generate total statistics from their DataBooks, through the use of a statistic module that handles this calculation. There are three type of statistic to be calculated and only on completed orders:

  • Profit Cost Revenue

It implements the following mode of operations:

  • default mode with no date input: generates on all completed order total profit, cost or revenue

    • The command enter by the user will be e.g for profit type generate-s s/PROFIT

  • mode with date input from the user: The statistic command takes in 2 dates, starting date and ending date and generates the profit, cost or revenue each month between this 2 dates.

    • command entered by the user will be e.g for profit type`generate-s s/PROFIT s1/YYYY.MM.DD s2/YYYY.MM.DD`

Here is the sequence of steps taken by SML when it receives a StatCommand:

StatisticsCalculationAcitvityDiagram
Statistic Module

The statistic module exposes the calculation operations in the Statistics interface. analogous to the output mode, there are 2 types of methods in this module, one that returns a String and the other a XYChart.Series<String, Number>.

  • Methods that return a String:

    • calculateTotalProfitOnCompleted(…​)

    • calculateTotalRevenueOnCompleted(…​)

    • calculateTotalCostOnCompleted(…​)

  • Methods that return a XYChart.Series<String, Number>:

    • calculateTotalProfitOnCompletedGraph(…​)

    • calculateTotalRevenueOnCompletedGraph(…​)

    • calculateTotalCostOnCompletedGraph(…​)

All methods were written using java8 Stream() feature. This is such that there are no loops in the code to make it more readable and maintainable.

org.apache.commons.math3.stat.StatUtils library is used to calculate the raw data inside a double[] (primitive double array). While the current v1.4 only use the sum to calculate the exact profit, revenue and cost, v2.0 implementation will make use of the linear regression / average methods inside the library to generate more advanced statistics.

Design Considerations / Alternative designs considered

Given that the number of orders in a seller database might be scaled to be very large in the future the main focus of this feature was to calculate statistics only when needed.

  • Alternative design :

    • To implement statistics, maintain a few running statistic counters upon loading of the main app, update these counters/values when the calculation is needed and then display it. This implementation would have been easy to implement within the existing AB3 code since it meant extending the Model class with a statistic model as compared to having the UI class to read from this value.

      • Pros: Easy to implement on top of AB3.

      • cons: Seller will be unable to obtain an instance of past statistics at each point without storing the history of the statistics somewhere since any change in the database/models will cause the values to change.

  • Alternative Design (current design) :

    • Statistics module will exist outside of the current packages and be standalone. Logic will handle communication with this module from UI. Respective Statistics will then be obtained from Logic to be displayed by the StatsWindow.

      • Pros: Ensure that the respective tasks are properly abstracted, adhering to the Single Responsibility Principle.

      • Pros: Being standalone means the way of calculating the statistics can be changed without affecting the rest of the codebase.

      • Cons: Logic will have to handle more calls and increases its responsibilities.

Design Decision 1:

Given that consideration, we decided that it will be the UI to trigger this call to Logic for calculation and then display the result of that call. Thus when Statistics need to be shown, the UI module will query the Logic for the respective data One of the motivations for this approach was to, as much as possible, maintain the architecture that is already in place in AB3. Furthermore, given that this feature requires date input from the user, we will need to find a way to get UI to send the data to make that query to logic. For the old AB3 implementation, CommandResult only contains feedback to the user with the actual changes on data done with a call to the model during execute(model).

Design Decision 2:

We decided to extend this command pattern by creating a statsPayload object. This object will hold the user queries for the statistic calculation (if there is any) and be bundled along with the CommandResult class to the UI to use in its query to logic. When the Ui executes the command to logic, the CommandResult is returned with this object, which the UI will then use to communicate to Logic. The resulting change is that the commandResult type will have slightly different behaviour.

All commands that are not statsCommand will call the default constructor of the commandResult class, where there is an Optional.empty() in place of the StatsPayload.

All statsCommand type will have to call the constructor of commandResult class and pass the StatsPayload object in. The key motivation behind this idea of a payload was to be able to encapsulate details about the calculation inside a single object, passed it to the appropriate place to be "unwrapped" when needed. This unwrapping is done by the mainWindow class in the application and subsequently, the data is passed to the statistic module.

A high-level view of the packages working together:

statsOverallClassDiagram

Current Implementation

Step-by-step breakdown

Below is a more in-depth explanation at each step.

Note that this breakdown is for the mode with date input. For the default mode with no date, the steps are almost the same, except that DefaultStatisticsWindow is called in place of StatisticsWindow.

Step 1: User inputs a stats command e.g generate-s s/REVENUE d1/2018.12.13 d2/2019.11.13.

The commandBox executes it and the MainWindow runs its executeCommand(commandText) method. Referring to the sequence diagram below, this results in logic.execute(commandText) being called and the statsCommandParser parses the input from the user, returning a Command object.

Seen here is the sequence diagram of the first step

StatsCommandSequenceDiagram1

Step 2: the logic then calls command.execute(command).

When this happens, the StatsCommand executes, which triggers a call to create a new StatsPayload. Then this is used to create a new CommandResult object and returns that to Logic, completing the execution of the user input command. Seen here is the sequence diagram of the second step.

StatsCommandSequenceDiagram2

Shown below is a quick summary of step 1-2 (Full complete diagram):

StatsCommandSequenceDiagram

Step 3: After the logic component completes its execution and return a CommandResult, the UI will call performUiChanges that handles the specific UI change. This then results in the statsPayload class being passed to Logic class and subsequently the statistics module, where the appropriate calculation will take place.

Step 4: Statistic Manager calculates the date.

Depending on the input, the Statistic Manager will calculate the value and return that, either in the form of a String or a XYChart.Series<String, Number>.

Step 5: with this output from logic, Statistic Window will then handle the displaying of the statistic, be it in chart form or string form. With this, the feature has finished executing! Attached below is the summary for step 3-5:

StatisticsCalculationUI

Current Version v1.4:

Here is a screenshot of the current product:

StatsExampleGraph

Limitations/Note to developers:

Developers working on adding features to this module should take note of these limitations that we have put in place for v1.4 of SML:

  • Date range starts from 1970 onwards

    • Date of year input starts from 1970 onwards. This is enough to fit our use case. Anything lower will be rejected.

  • Extension of additional Calculation methods

    • To easily extend the behaviour with additional methods to calculate, reference the current implementation in StatisticsManager; There are utility methods to help you with extracting the relevant information from your data.

  • UI Changes

    • Any additional Ui Changes should be handled by the controllers for the UI classes StatisticWindow and DefaultStatisticWindow class.