When disaster hits a populated area, the most critical task is to provide immediately affected people with what they need as quickly and as efficiently as possible.

This project completes an application that manages the list of goods that need to be shipped to the disaster area. The client application tracks the quantity of items needed, tracks the quantity on hand, and stores the information in a file for future use.

The types of goods that need to be shipped are of two categories;

  • Non-Perishable products, such as blankets and tents, which have no expiry date. We refer to products in this category as Product objects.
  • Perishable products, such as food and medicine, that have an expiry date. We refer to products in this category as Perishable.

To complete this project you will need to create several classes that encapsulate your solution.

OVERVIEW OF THE CLASSES TO BE DEVELOPED

The classes used by the client application are:

  • Date: A class to be used to hold the expiry date of the perishable items.
  • ErrorState: A class to keep track of the error state of client code. Errors may occur during data entry and user interaction.
  • Product: A class for managing non-perishable products.
  • Perishable: A class for managing perishable products. This class inherits the structure of the "Product" class and manages an expiry date.
  • iProduct: An interface to the Product hierarchy. This interface exposes the features of the hierarchy available to the client application. Any "iProduct" class can
    • read itself from or write itself to the console
    • save itself to or load itself from a text file
    • compare itself to a unique C-string identifier
    • determine if it is greater than another product in the collating sequence
    • report the total cost of the items on hand
    • describe itself
    • update the quantity of the items on hand
    • report its quantity of the items on hand
    • report the quantity of items needed
    • accept a number of items
    • Using this class, the client application can
    • save its set of iProducts to a file and retrieve that set later
    • read individual item specifications from the keyboard and display them on the screen
    • update information regarding the number of each product on hand

THE CLIENT APPLICATION

The client application manages the iProducts and provides the user with options to

  • list the Products
  • display details of a Product
  • add a Product
  • add items of a Product
  • update the items of a Product
  • delete a Product
  • sort the set of Products

MILESTONE 5: THE PERISHABLE CLASS AND AMA APPLICATION

For milestone 5 you must expand the Product hierarchy with the class Perishable, and create an application that ties everything together.

PERISHABLE CLASS

Define a new class called Perishable derived from the Product class in the ama namespace. Store the definition in a file named Perishable.h and implement the function members in a file named Perishable.cpp. Because it inherits the structure of the class Product, Perishable has all the attributes from it, although they will not be accessible; in order to access inherited attributes use member functions.

Add to this class the following members:

  • An attribute of type Date to store the expiry date for a perishable product
  • A default constructor. This constructor should call the one-parameter constructor from the base, and pass 'P' for the record tag.
  • An override for the write() function from the base class.
    • This function calls its base class version, passing the arguments it receives.
    • If the object is in error mode or in an empty state, this function does nothing further and returns.
    • Otherwise, this function inserts into the stream the expiry date. The format of the date depends on the parameter writeMode.
    • If writeMode is write_human, the format is Expiry Date:EXPIRY< ENDL>
    • The label is displayed in a field of size max_length_label, aligned to the right. The field size includes the space () after the colon (:).
    • If writeMode is write_table, the format is _EXPIRY_|
    • If writeMode is write_condensed, the format is ,EXPIRY
  • An override for the read() function from the base class.
    • This function calls its base class version, passing the arguments it receives. The base class version should extract data from the stream for all attributes with the exception of the expiry date.
    • If the data should be extracted from the stream in interactive mode (the second parameter is true), this function should ask for the date and use the extraction operator from the Date class to get the expiry date, like this: Expiry date (YYYY/MM/DD):_< User Types Here>
    • The text prompted to the user will be displayed in a field of size max_length_label, aligned to the right. The field size includes the space (_) after the colon (:).
    • If there are errors while reading the date (the status of the read date is different from no_error), this function should call istream::setstate(ios::failbit) to set the failure bit of the first parameter, and sets the ErrorState attribute to one of the following messages (use a function from the base class to set the error message):
      Invalid Year in Date Entry
      Invalid Month in Date Entry
      Invalid Day in Date Entry
      Invalid Date Entry
    • If there are no errors reading the date, this function stores the date read in its expiry attribute, otherwise the expiry attribute should remain in an empty state.
    • oIf the data should be extracted from the stream in non-interactive mode (the second parameter is false), this function should call the Date::read(). Note that Date::read() doesn't extract the '\n' from the stream, so you should also call istream::ignore().

CHANGES IN UTILITIES MODULE

Modify the implementation of the following function:

  • iProduct* createInstance(char tag): This function is responsible to dynamically create instances in the Product hierarchy.
  • If the parameter has the value 'N' or 'n', this function should dynamically create an object of type Product using the default constructor and return its address.
  • If the parameter has the value 'P' or 'p', this function should dynamically create an object of type Perishable using the default constructor and return its address.
  • If the parameter has any other value, this function should return null.

SORTING

Add to the project a header file named Sort.h. In this file, add to the namespace sict a template function called sort(), that receives two parameters:

  • The first parameter is an array of a template type T.
  • The second parameter is the size of the array.

This function should sort the elements of the array in ascending order. For a refresh on how to sort an array of integers (and an example), read the chapter https://cs.senecac.on.ca/~btp100/pages/content/sorts.html.

AMAAPP CLASS

The AmaApp class has several private member functions and only two public functions.

A description for each function is provided below. Those that are more complex will be complimented with suggested pseudo code to help you implement the function. You may use the pseudo code as suggested, revise it to improve on the logic, or implement your own logic.

Code the AmaApp class in files AmaApp.h and AmaApp.cpp.

Add to the class the following private member variables:

  • char m_filename[256]: Holds the name of the text file used to store the product information.
  • iProduct* m_products[100]: An array of iProduct pointers (i.e. each element of this array is a pointer to a dynamically allocated instance of type iProduct).
  • int m_noOfProducts: Number of products (perishable or non-perishable) pointed to by the m_products array.

Add to the class the following public member functions:

  • A custom constructor that receives as parameter an array of characters representing the filename used by the application. This constructor should:
    • Copy filename to m_filename member variable (assume the string is valid)
    • Set all the m_product elements to null
    • Set m_noOfProducts to zero
    • Load the records from the file (call a member function to do this).
  • A destructor that deallocates all dynamic instances stored in the m_products array.
  • int run():

Add to the class the following private member functions:

  • Make sure the AmaApp cannot get copied or assigned to another AmaApp.
  • void pause() const: A query that prints: "Press Enter to continue..."< ENDL> then waits for the user to hit enter. If the user hits any other key before pressing enter, the key is ignored.
  • int menu() const: This function displays the menu shown below and waits for the user to select an option (note that marks a blank space):
Disaster Aid Supply Management Program
--------------------------------------
1- List products
2- Search product
3- Add non-perishable product
4- Add perishable product
5- Add to product quantity
6- Delete product
7- Sort products
0- Exit program
>_
  • If the selection is valid, this function will return the selection, otherwise it will return -1.
  • The standard input buffer (keyboard) must be cleared before the function exits.
  • The standard input buffer (keyboard) must be cleared before the function exits.
    • If the file exists, read the data file and store each record in the array of products.
    • After reading all the records, close the file.
    • Pseudo code:
      Delete all products from the array (if any)
      Set reading index to zero
      Open the file for reading (use ios::in)
      if the file is open, then
      until reading fails loop
      read one char character to identify type of Product (the tag)
      call the ama::createInstance(tag) to create an instance
      if createInstance(tag) returns not null
      store the product in the array at the read index
      skip the comma from the file
      read the product from the file (non-interractive mode)
      increment the read index
      continue the loop
      set number of products to readIndex
      close the datafile
  • void saveProductRecords() const: Loops through the m_products array up to m_noOfProducts and stores each of them in a file (the name of the file is stored in an attribute; use the function iProduct:: write(), passing ama::write_condensed as the second parameter). After each record, this function adds an end line into the file.
  • void listProducts() const: Prints the following title:
------------------------------------------------------------------------------------------------
| Row | SKU | Product Name | Unit | Price | Tax | QtyA | QtyN | Expiry |
|-----|---------|------------------|------------|---------|-----|--------|--------|------------|
  • Then loops through the m_products array up to m_noOfProducts and prints a bar (|), followed by the row number (four spaces wide, right aligned), followed by a space and a bar/pipe character (|). Then prints the current product and a newline. To insert the content of the current product, call iProduct::write(), using ama::write_table for the second parameter.
    • At each iteration, it will calculate the total cost of the products in a double value using the operator += (free helper for the iProduct interface).
    • When the list is done, the table footer will be printed:
------------------------------------------------------------------------------------------------
| Total cost of support ($): | XXXXXXX.XX |
------------------------------------------------------------------------------------------------
  • The total cost value is printed on a field of size 10, aligned to the right and two digits precision.
  • Before returning, this function calls AmaApp::pause().
  • void deleteProductRecord(iProduct* product): Loops through the m_products array up to m_noOfProducts and stores each of them in a file, but skips the product passed as parameter (the name of the file is stored in an attribute; use the function iProduct:: write(), passing ama::write_condensed as the second parameter). After each record, this function adds an end line into the file.
  • void sort(): calls the template function sict::sort(), passing the array of products as parameter.
  • iProduct* find(const char* sku) const: Loops through the m_products array up to m_noOfProducts and checks each of them for the same SKU as the incoming argument using the operator == implemented by the Product class.
    • If a match is found, this function returns the address of the found Product, otherwise returns null.
  • void addQty(iProduct* product): Updates the quantity on hand for an iProduct.
    • This function displays the parameter in human readable form, followed by two end lines; then asks for an integer for quantity purchased: Please enter the number of purchased items: _ < User Types Here>
    • If it cannot read the integer, clears the stream error, prints a message and returns: Invalid quantity value!< ENDL>
    • If it can read the integer, it makes sure the amount is less or equal than the amount required (i.e. qtyNeeded() - qtyAvailable()). If it is less than or equal, it will add the value to the quantity on hand of the product using the operator += implemented by the Product class. If the value is not less than or equal the amount needed, it will only accept the amount required and prints a message to return the extra: Too many items; only X is needed. Please return the extra Y items.< ENDL>
    • Lastly, save all records back to the file and display a message: < ENDL>Updated!< ENDL>
    • Make sure after the entry the keyboard is flushed.
  • void addProduct(char tag): This function should add a new product at the end of the array, by calling ama::createInstance(tag). If a new instance is successfully created, this function should use the extraction operator (helper for iProduct) to initialize the product's attributes from the keyboard.
    • If the extraction fails, clear the error from the input stream, display the product to screen (to show the error message), in the following format: < ENDL>PRODUCT< ENDL>< ENDL>
    • If the extraction succeeds, add the product at the end of the array, call saveProductRecords() to update the file and print: < ENDL> Success!< ENDL>
  • int run(): Display the menu, receive the user's selection, and do the action requested (follow with a pause using the pause() function), and repeat (redisplay the menu ) until the user selects zero to exit.

1-List products
List the products; call AmaApp::listProducts();

2-Display product
Ask for a SKU using the prompt Please enter the product SKU:_ then search for the product. If found, display the product information in human readable form, otherwise display No such product!< ENDL>. Follow with a pause, and display the menu again.

3-Add non-perishable product
Add a non-perishable product to the system using the addProduct function, and load all records from the file.

4-Add perishable product
Add a perishable product to the system using the addProduct function, and load all records from the file.

5-Add to quantity of purchased products
Ask for a SKU using the prompt Please enter the product SKU:_ then search for the product. If found, call AmaApp:: addQty() function to add quantity, otherwise display No such product!< ENDL>. Before displaying the menu again, print an end line.

6-Delete product
Ask for a SKU using the prompt Please enter the product SKU: _ then search for the product. If found, call AmaApp:: deleteProductRecord() function, reload the products from the file and display Deleted!< ENDL>; otherwise display No such product!< ENDL>.

7-Sort products
Call AmaApp::sort() to sort the products from the array, followed by AmaApp::saveProductRecords() to store the array in the file; then display Sorted!< ENDL>< ENDL>

0- Exit program
The program will print Goodbye!< ENDL> and then terminates (function AmaApp::run() returns).

In case of invalid menu selection the program will print ~~~Invalid selection, try again!~~~< ENDL>, followed by a pause. This function should return 0 when it ends.

You are provided with a file named inventory.txt that contains a list of perishable and non-perishable items. Add this file in your project folder. The function AmaApp::loadProductRecords() should be able to load the content of this file.

Academic Honesty!
It is not our intention to break the school's academic policy. Posted solutions are meant to be used as a reference and should not be submitted as is. We are not held liable for any misuse of the solutions. Please see the frequently asked questions page for further questions and inquiries.
Kindly complete the form. Please provide a valid email address and we will get back to you within 24 hours. Payment is through PayPal, Buy me a Coffee or Cryptocurrency. We are a nonprofit organization however we need funds to keep this organization operating and to be able to complete our research and development projects.