Uploaded by Ayat

C++ Supermarket System Code Snippet

advertisement
#include "SupermarketSystem.h"
This includes my own header file containing class declarations and function prototypes. I need this
because it contains the blueprint for my SupermarketSystem class. Without it, the compiler wouldn't
know about my class structure.
#include <iostream> This provides input/output operations. I Use this for:
cout << "Product ID: ";
cin >> p.productId;
// Display prompts
// Get user input
#include <fstream> This enables file operations. I need this for reading and writing to my inventory
and transaction files:
// Reading inventory
ofstream file(TRANSACTIONS_FILE, ios::app); // Writing transactions
ifstream file(INVENTORY_FILE);
#include <sstream> This helps with string manipulation and conversion. I use this when parsing Ir
pipe-delimited files:
stringstream ss(line);
getline(ss, p.productId, '|');
// Split line by '|' delimiter
#include <iomanip> This provides output formatting capabilities. I use this for displaying tables nicely:
cout << setw(10) << fixed << setprecision(2) << price;
// Format prices
#include <vector> This provides dynamic arrays. I use this for storing products and bill items:
// Store products
vector<pair<string, int>> currentBill; // Store bill items
vector<Product> inventory;
#include <string> This provides string handling. I use this throughout for text data:
string productId;
string name;
// Store product ID
// Store product name
#include <windows.h> This provides Windows-specific functions. I use this for:
// For loading animations
system("cls"); // For clearing screen
Sleep(1500);
#include <ctime> This provides time-related functions. I use this for:
// Get current time for transactions
p.lastRestocked = time(nullptr); // Record restock time
time_t now = time(nullptr);
SupermarketSystem::SupermarketSystem() {
// This is where I actually USE those tools
readInventory();
// Uses <fstream> to read inventory.txt
readTransactions(); // Uses <fstream> to read transactions.txt
// Initialize my vectors for storing data
// Set up my display formatting
}
SupermarketSystem::~SupermarketSystem() {
// This is where I clean up what I set up
saveInventory();
// Uses <fstream> to save final inventory state
closeTransactionLog(); // Uses <fstream> to close any open files
}
1. The clearScreen Function:
void SupermarketSystem::clearScreen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
Think of this like erasing a whiteboard before writing new information, it handles different
operating systems:
• #ifdef _WIN32 checks if you're using Windows
• If I are, it uses system("cls") (Windows command)
• If not, it uses system("clear") (Unix/Linux command)
•
The displayTransactionHeader Function:
void SupermarketSystem::displayTransactionHeader() {
cout << setfill('-') << setw(80) << "-" << endl;
// Creates top border
cout << setfill(' ');
// Reset fill character
cout << left
// Align text to left
<< setw(22) << "Date & Time" << "|"
// 22 spaces for date
<< setw(6) << "ID" << "|"
// 6 spaces for ID
<< setw(8) << "Type" << "|"
// 8 spaces for type
<< setw(8) << "Counter" << "|"
// 8 spaces for counter
<< "Transaction" << endl;
// Rest for transaction
This first part creates the table's top border. Imagine drawing a line on paper:
• setfill('-') means "use dashes instead of spaces"
• setw(80) means "make it 80 characters wide"
• After using a custom fill character (e.g., '-' for the top border), it’s good practice to
reset the fill character back to its default value (a space ' ') to avoid unexpected
formatting in subsequent output.
• SupermarketSystem::displayInventoryHeader() uses this format because it's
defining a function that belongs to the SupermarketSystem class. It's like saying
"this is the displayInventoryHeader function that belongs to SupermarketSystem."
Think of it like addressing a letter - "Johnson::House" means "the House that
belongs to the Johnson family."
void SupermarketSystem::displayTransaction(...):
void SupermarketSystem::displayTransaction(const string& datetime, const string& id,
const string& type, const string& counter,
const string& transaction) {
cout << left
<< setw(22) << datetime << "|"
<< setw(6) << id << "|"
<< setw(8) << type << "|"
<< setw(8) << counter << "|"
<< transaction << endl;
}
Purpose: This is a member function of the SupermarketSystem class used to display
formatted transaction details.
Parameters:
• datetime: A timestamp for the transaction (e.g., 2024-12-21 15:45).
• id: An identifier for the transaction (e.g., TX123).
• type: The type of transaction (e.g., Sale, Return).
• counter: The counter or register where the transaction occurred (e.g., C1).
• transaction: Additional information about the transaction.
• transaction: Not formatted with setw() because it might be variable in length and
can extend naturally to the end of the line.
• << "Contact" << endl; This gets whatever space is left.
•
void SupermarketSystem::displayInventoryHeader() {
cout << setfill('-') << setw(120) << "-" << endl;
cout << setfill(' ');
cout << left
<< setw(6) << "ID" << "|"
<< setw(16) << "Item Name" << "|"
<< setw(11) << "Category" << "|"
<< setw(6) << "Cost" << "|"
<< setw(6) << "Price" << "|"
<< setw(6) << "Stock" << "|"
<< setw(4) << "Min" << "|"
<< setw(4) << "Max" << "|"
<< setw(12) << "Expiry" << "|"
<< setw(10) << "Location" << "|"
<< setw(8) << "Store" << "|"
<< "Contact" << endl;
cout << setfill('-') << setw(120) << "-" << endl;
cout << setfill(' ');
}
void SupermarketSystem::displayInventoryItem(const string& id, const string& name,
const string& category,const string& cost, const string& price, const string&
stock,const string& min, const string& max, const string& expiry,const string&
location, const string& store, const string& contact) {
cout << left
<< setw(6) << id << "|"
<< setw(16) << name << "|"
<< setw(11) << category << "|"
<< setw(6) << cost << "|"
<< setw(6) << price << "|"
<< setw(6) << stock << "|"
<< setw(4) << min << "|"
<< setw(4) << max << "|"
<< setw(12) << expiry << "|"
<< setw(10) << location << "|"
<< setw(8) << store << "|"
<< contact << endl;
}
Why Two Separate Functions? Think of it like creating a table in Excel:
• displayInventoryHeader() is like creating the column titles at the top
• displayInventoryItem() is like adding each row of data
We separate them because:
• The header only needs to be displayed once at the top
• The item display function will be called multiple times, once for each product, header
once, then use the item template for each product.
void displayInventoryItem(string id) {
// Makes a copy of the string - slower and uses more memory}
// With const string&:
void displayInventoryItem(const string& id) {
// Just looks at the original - faster and more efficient}
void SupermarketSystem::displayAllInventory() {
string line;
ifstream file("inventory.txt");
if (file.is_open()) {
displayInventoryHeader();
// Display the header first
while (getline(file, line)) {
// Skip empty lines
if (line.empty()) continue;
// Parse the line (assuming | as delimiter)
stringstream ss(line);
string id, name, category, cost, price, stock, min, max, expiry,
location, store, contact;
getline(ss, id, '|');
getline(ss, name, '|');
getline(ss, category, '|');
getline(ss, cost, '|');
getline(ss, price, '|');
getline(ss, stock, '|');
getline(ss, min, '|');
getline(ss, max, '|');
getline(ss, expiry, '|');
getline(ss, location, '|');
getline(ss, store, '|');
getline(ss, contact);
// Display the formatted inventory item
displayInventoryItem(id, name, category, cost, price, stock,
min, max, expiry, location, store, contact);
}
file.close();
} else {
cout << "Unable to open inventory file" << endl;
}
}
- it's like a master function that brings together the header and item display functions we
just discussed.
Setting Up:
string line;
ifstream file("inventory.txt");
We need this because my supermarket system stores all data in files rather than temporary
memory. The string line is declared first because we'll read the file line by line - it's like
having a temporary holder for each row of data. ifstream specifically opens the file for
reading (not writing) because we only want to display data, not modify it.
Safety Check:
if (file.is_open()) {
displayInventoryHeader();
This check is crucial for preventing crashes. If someone accidentally deleted inventory.txt
or moved it, my program would crash without this check. We display the header first inside
this check because there's no point showing column titles if we have no data to display.
Reading Each Line:
while (getline(file, line)) {
if (line.empty()) continue;
The while loop with getline is essential because inventory files can have any number of
products. This makes my program flexible - it works whether I have 5 products or 5000. The
empty line check prevents formatting issues if someone accidentally adds blank lines to
the file.
Breaking Down Each Line:
stringstream ss(line);
string id, name, category /*...etc*/;
We use stringstream because it's the most reliable way to split text by a delimiter (|). We
declare separate string variables for each field because:
Imagine you have a line in your inventory.txt file that looks like this:
1001|Coca Cola|Beverages|1.50|2.00|100|10|200|2024-12-31|Shelf A|Main Store|12345
First, we create a stringstream. Think of a stringstream like a special tool that can take this
long line of text and help us cut it into pieces:
stringstream ss(line);
split it
// Now ss contains our line and can help us
Then, we need places to store each piece of information. That's why we create separate
string variables:
string name;
// Will store "1001"
// Will store "Coca Cola"
string category;
// Will store "Beverages"
string id;
// and so on...
• stringstream: Converts between strings and other data types (e.g., int, float).
• ss: An instance of stringstream that holds the string data and allows extraction
(>>) or insertion (<<).
Parsing the Data:
getline(ss, id, '|');
// Gets everything up to the first |
getline(ss, name, '|'); // Gets everything between first and second |
Using getline with a delimiter lets us handle product names or locations that might
contain spaces. If we used simpler splitting methods, names like "Coca Cola" would break
our formatting. The | delimiter was chosen because it's unlikely to appear in actual product
data.
1. "Read everything until you see a | and put it in id"
2. "Now read everything until the next | and put it in name"
Why is this better than other methods? Let's say we tried to split by spaces instead of |:
"Coca Cola|Beverages"
If we split by spaces, we'd get:
• "Coca"
• "Cola|Beverages"
But using getline with | as delimiter, we correctly get:
• "Coca Cola"
• "Beverages"
Displaying the Item:
displayInventoryItem(id, name, category /*...etc*/);
We pass each piece separately rather than passing the whole line because:
1. It allows format checking for each field
2. Makes it easier to modify how individual columns are displayed
Error Handling:
} else {
cout << "Unable to open inventory file" << endl;
}
This helps in troubleshooting - if users see this, they know to check if the file exists and is
in the right location.
UI (User Interface) functions and why you needed each one:
1. showConfirmDialog(const string& action)
showConfirmDialog("add a new product to inventory");
// Makes sure user meant to do this by showing:
//
"Are you sure you want to add a new product to inventory?"
2. showSuccessDialog/showErrorDialog
if(saveInventory()) {
showSuccessDialog("Product added successfully!");
} else {
showErrorDialog("Failed to save to inventory file!");
}
These tell the user if their action worked or failed. Without these, users wouldn't know if
their actions succeeded.
you actually declare showSuccessDialog and showErrorDialog later in your code file as
regular functions:
void showSuccessDialog(const string& message) {
cout << "\n+----------------------------------------+\n";
cout << "|
SUCCESS
void showErrorDialog(const string& error) {
|\n";
3. drawSimpleFrame() & drawSimpleBox()
// Creates the main menu border
drawSimpleBox("Add New Product"); // Creates section headers
drawSimpleFrame();
4. drawSimpleTable()
// Creates formatted tables for displaying data:
vector<string> headers = {"ID", "Name", "Price"};
vector<int> widths = {5, 20, 10};
drawSimpleTable(headers, widths);
vector is - it's like a dynamic array that can hold multiple values of the same type. In your
case, you're using two types of vectors:
This function takes two parameters:
1. const vector<string>& headers: A list of column titles
2. const vector<int>& widths: A list of how wide each column should be
The & means "pass by reference." You learned this is more efficient than copying the
whole vector. The const before it means "don't change these values inside the function."
Two vectors must match - if you have 3 headers, you need 3 widths
• "ID" gets 5 spaces
• "Name" gets 20 spaces
• "Price" gets 10 spaces
Then you learned to call the function by passing both vectors:
drawSimpleTable(headers, widths);
5. showLoadingAnimation()
showLoadingAnimation("Saving data"); // Shows: "Saving data..."
users feedback when the program is working, so they know it hasn't frozen.
6. showMainMenu()
int choice = showMainMenu();
//Shows opts & gets user's choice
Here are different approaches:
1. Using newlines (\n) alone (harder way):
cout << "\n\n
CONGRATULATIONS!
\n\n";
// This is tricky because you have to count spaces manually
2. Using | as guides (better):
cout << "|
CONGRATULATIONS!
|\n";
// The | characters help you see if it's centered
3. Using setw() (best practice):
cout << setw(50) << right << "CONGRATULATIONS!" << endl;
// This automatically centers the text in 50 spaces
// Table Drawing Functions
void drawSimpleTable(const vector<string>& headers, const vector<int>& widths) {
// Draw header line
cout << string(4, ' ');
for(size_t i = 0; i < headers.size(); i++) {
cout << left << setw(widths[i]) << headers[i] << " ";
}
cout << "\n" << string(4, ' ');
cout << "Hello, World!" << endl;
cout << string(4, ' '); // Creates 4 spaces for left margin
cout << "This is indented by 4 spaces." << endl;
Hello, World!
This is indented by 4 spaces.
First, remember that we have these two vectors:
vector<string> headers= {"ID", "Name", "Price"}; // Your col titles
vector<int> widths = {5, 20, 10};
// How wide each column should be
Now, the for loop:
for(size_t i = 0; i < headers.size(); i++)
• size_t is just a type of number that's good for counting things (like array positions)
• i is like a counter - it starts at 0 and goes up by 1 each time through the loop
• Every vector in C++ comes with a built-in function called .size() - it's like a counter
that tells you how many items are in the vector. So when we write headers.size(),
we're asking "how many items are in our headers vector?" In this case, it would return
3 because we have three items: "ID", "Name", and "Price".
• So this loop will run 3 times, with i being 0, then 1, then 2
Inside the loop:
cout << left << setw(widths[i]) << headers[i] << " ";
First Loop (i = 0):
1. widths[0] is 5, so setw(widths[i]) means "make a space 5 characters wide"
2. headers[0] is "ID"
3. left means "align ID to the left of its 5-character space"
Second Loop (i = 1):
4. widths[1] is 20, so setw(widths[i]) means "make a space 20 characters wide"
5. headers[1] is "Name"
6. left means "align Name to the left of its 20-character space"
The result looks like this:
ID
Name
Price
------------------------------------// Draw separator
int totalWidth = 0;
for(int width : widths) totalWidth += width;
cout << string(totalWidth + headers.size() - 1, '-') << "\n";
for(int width : widths) // This is a "range-based for loop"
totalWidth += width; // Adds each width to totalWidth
This loop calculates the total width of all columns in the table by iterating through a vector
widths, which contains the width of each column. // Total Width: 45
Why this syntax? It's a shorter way to write:
for(int i = 0; i < widths.size(); i++) {
totalWidth = totalWidth + widths[i];
}
cout << string(totalWidth + headers.size() - 1, '-') << "\n";
Syntax breakdown:
•
•
•
•
string(X, '-') creates a string of X dashes
totalWidth is sum of all column widths
headers.size() - 1 accounts for spaces between columns
Result creates the exact number of dashes needed to underline your table
Real reason I used this: If your columns are 5, 20, and 10 wide:
•
•
•
totalWidth = 35 (5+20+10)
headers.size() - 1 = 2 (spaces between 3 columns)
So it makes 37 dashes, perfectly matching your header row width
The main reason is data presentation. In a supermarket system, you need to show lots of information (like
products, prices, stock levels) in a way that's easy to read. Imagine if your data was displayed like this:
ID1234 Coca Cola 2.99 50
ID5678 Bread 1.99 30
That's hard to read right? What you wanted instead was something like this:
ID
Product
Price
Stock
----------------------------1234
Coca Cola
2.99
50
5678
Bread
1.99
30
1. First part (header line):
cout << string(4, ' ');
// Gives some space from the left edge
for(size_t i = 0; i < headers.size(); i++) {
cout << left << setw(widths[i]) << headers[i] << " ";
}
This creates perfectly spaced column headers where each column is exactly wide enough for its data.
2. Second part (separator):
int totalWidth = 0;
for(int width : widths) totalWidth += width;
cout << string(totalWidth + headers.size() - 1, '-');
This creates a line of dashes that perfectly matches the width of your headers, making your table look
professional.
void showConfirmDialog(const string& action)
// Takes what action user wants to confirm
void showSuccessDialog(const string& message) // Takes what succeeded
void showErrorDialog(const string& error)
// Takes what went wrong
void showWarningDialog(const string& message) // Takes what to warn about
The key difference is how each one uses its message:
1. Confirm Dialog is special - it asks a question and waits for input:
cout << "
Are you sure you want to " << action << "?\n";
cout << "
Press Y to confirm, Any other key to cancel: ";
2. The others just show their message:
cout << "
" << message << "\n";
Loading Animation Function:
void showLoadingAnimation(const string& message) {
cout << message;
for(int i = 0; i < 3; i++) {
Sleep(300);
cout << ".";
// Pauses for 300 milliseconds
// Prints one dot
}
cout << "\n";
// New line at the end
}
You chose this syntax because:
•
•
•
You needed to show users something is happening when your program is working (like saving files)
Sleep(300) creates a delay so dots appear one at a time: "Saving." then "Saving.." then "Saving..."
The for loop repeating 3 times means three dots will appear
•
const string& message lets you reuse this animation with different messages without copying
strings
2. Clear Input Buffer Function:
void clearInputBuffer() {
// Clears any error flags
cin.ignore(1000, '\n'); // Skips up to 1000 chars until newline
cin.clear();
}
You needed this function because:
•
•
•
•
When users enter data wrong (like letters where numbers should be), it can mess up your next input
cin.clear() resets any error flags so your program can try reading input again
cin.ignore(1000, '\n') removes any leftover characters they typed
Without this, your program might skip asking for input or read wrong values
For example, when you use them:
showLoadingAnimation("Saving inventory");
clearInputBuffer();
right
// Shows: Saving inventory...
// Cleans up after bad input so your next prompt works
Table Formatting Constants
const vector<string> INVENTORY_HEADERS = {"ID", "Name", "Category",
"Price", "Stock", "Expiry"};
const vector<int> INVENTORY_WIDTHS = {8, 20, 12, 10, 8, 12};
You chose to make these constant vectors because:
•
•
•
These define how your tables will look throughout the program
Making them constants (const) means they can't be accidentally changed
Each header has a matching width (like "Name" gets 20 spaces)
When displayed, it creates a table that looks like this:
ID
Name
Category
Price
Stock
Expiry
-----------------------------------------------------------------------1001
Coca Cola
Beverages
2.99
50
2024-12-31
7757
Roshid Tears
Other
1.00
5
2030-12-20
3
Hotdog
Meat
4.00
40
2024-14-12
For bills, you used different constants:
const vector<string> BILL_HEADERS = {"Product", "Quantity", "Price", "Total"};
const vector<int> BILL_WIDTHS =
{25,
10,
Price
Total
Which creates a bill display like this:this:
Product
Quantity
------------------------------------------------------------
1. enum Category
enum Category {
GROCERY,
10,
15};
DAIRY,
MEAT,
// ...
};
I used this because:
•
I needed a way to make sure products can only be in specific categories
•
Instead of letting someone type "grocry" (misspelled) or "GROCERY" vs
"Grocery", this gives them fixed choices
•
It's like creating a dropdown menu - you can only pick from these
options
•
The computer turns these into numbers (GROCERY becomes 0, DAIRY becomes
1, etc.) which makes comparing categories faster
2. struct Product
struct Product {
string productId;
string name;
Category category;
double price;
// ...
};
I needed this because:
•
Every product in a supermarket has many pieces of information
•
Instead of creating separate variables for each piece, this groups them
together
•
It's like creating a form where each product fills in:
o ID number (productId)
o Name
o Which category it belongs to (from our enum)
o Price (using double for decimal numbers like 2.99)
o How many we have (quantity)
o When it expires
o Where it's stored
o When we last got more
Think of it like a product card - instead of having information scattered
everywhere, everything about one product stays together.
1. The Class Declaration
class InventoryManager {
private:
vector<Product> inventory;
I needed this because:
•
vector<Product> is like a list that can grow or shrink
•
Instead of having a fixed size array, I can add or remove products
easily
•
Each item in this list is a Product (with all those details we defined
in struct)
2. The File Constants
const string INVENTORY_FILE = "inventory.txt";
const string TRANSACTIONS_FILE = "transactions.txt";
I used these because:
•
I need to save my data somewhere permanent (not just in computer memory)
•
Making them const means I can't accidentally change the filenames
•
inventory.txt stores all my products
•
transactions.txt keeps track of what's been sold
void verifyFilePath() {
char currentPath[FILENAME_MAX];
// ANSI string
•
char currentPath[FILENAME_MAX] creates a character array to store the path
•
I used char array instead of string because Windows functions expect
this older format
•
FILENAME_MAX is a constant that tells us the maximum length Windows
allows for file paths
if (GetCurrentDirectoryA(FILENAME_MAX, currentPath))
I used this because:
•
GetCurrentDirectoryA is a Windows function that finds where my program
is running
•
The 'A' at the end means it uses ANSI characters (basic text)
•
FILENAME_MAX tells it how much space we have in our array
•
currentPath is where it will store the folder path it finds
•
The if checks if it successfully found the path
{
cout << "Current working directory: " << currentPath << "\n";
cout << "Saving inventory to: " << currentPath << "\\" << INVENTORY_FILE <<"\n";
} else {
cout << "Error retrieving current directory!" << endl;
}
If it works:
•
Shows the folder path it found
•
Shows where it will save inventory.txt If it fails:
string categoryToString(Category cat)
I needed this function because:
•
Remember our enum Category? It stores categories as numbers (GROCERY =
0, DAIRY = 1, etc.)
•
But when showing it to users or saving to files, we want actual words
like "Grocery", not "0"
•
This function converts those numbers back to text that makes sense to
humans
The switch part:
switch(cat) {
case GROCERY: return "Grocery";
case DAIRY: return "Dairy";
//...
}
I used switch because:
•
It's the clearest way to say "if the category is GROCERY, show 'Grocery'"
•
Each case matches one of our enum values
•
return immediately gives back the right text
•
default: return "Other" catches any category we didn't expect
// Computer sees this as 0
string display = categoryToString(cat); // Converts 0 to "Grocery"
cout << display; // Shows "Grocery" to user
Category cat = GROCERY;
Category stringToCategory(const string& cat)
I needed this function because:
•
It's the opposite of categoryToString
•
When reading from files or getting user input, we get text like "Grocery"
•
But our program needs to convert this text back into our enum numbers
•
const string& means we're borrowing the text without making a copy (saves
memory)
The conversion part:
if(cat == "Grocery") return GROCERY;
if(cat == "Dairy") return DAIRY;
//...
I used if statements because:
•
It's checking text exactly: "Grocery" must match exactly
•
Each line converts a text category back to our enum value
•
If nothing matches, we return OTHER (it's our safety net)
// When reading from inventory.txt, we get:
"Milk|Dairy|2.99|..."
// This function converts "Dairy" to the DAIRY enum value
// so our program can use it in calculations and comparisons
1. Constructor (The Starting Point)
InventoryManager() {
loadInventory();
}
I needed this because:
•
As soon as my inventory system starts, it should load existing products
•
It
automatically
calls
loadInventory()
when
InventoryManager
2. loadInventory Function
ifstream file(INVENTORY_FILE);
if(!file) {
showWarningDialog("No inventory file found...");
you
create
an
return;
}
This checks if our inventory file exists. If not, warns the user.
3. Reading Each Product
while(getline(file, line)) {
stringstream ss(line);
Product p;
I needed this because:
•
Reads one line at a time from the file
•
Each line contains one product's information
•
stringstream helps split the line at | symbols
4. Parsing Different Parts
getline(ss, p.productId, '|');
getline(ss, p.name, '|');
getline(ss, category, '|');
I did this because:
•
Each product detail is separated by | in the file
•
Like: "1001|Coca Cola|Beverages|2.99..."
•
getline with '|' splits at each | mark
5. Converting Numbers
getline(ss, temp, '|'); p.price = stod(temp);
I needed this because:
•
Numbers in the file are text ("2.99")
•
stod converts text to decimal numbers (2.99)
•
stoi converts text to whole numbers
6. Error Handling
try {
// parsing code
} catch(const exception& e) {
showErrorDialog("Error parsing inventory entry...");
This prevents the program from crashing if:
•
A file line is badly formatted
•
Numbers are invalid
•
Required information is missing
Parsing Remaining Fields:
getline(ss, p.expiryDate, '|');
getline(ss, p.supplier, '|');
getline(ss, p.location, '|');
getline(ss, lastRestocked);
// Gets expiry date until | character
// Gets supplier name until | character
// Gets location until | character
// Gets last restocked time (no | because
it's last)
Syntax:
•
•
getline(where to read from, where to store, delimiter)
Last getline doesn't need '|' because it's the end of line
Converting Special Values:
p.category = stringToCategory(category);
p.lastRestocked = stoll(lastRestocked);
// Convert text to enum value
// Convert string to long long
number
Needed because:
•
category needs to be converted from text ("Grocery") to enum (0)
• stoll converts text timestamp to a number our program can use
Adding to Inventory:
inventory.push_back(p);
// Adds product to our vector list
Syntax:
•
push_back adds new item to end of vector
•
p is the product we just created
Error Handling:
catch(const exception& e) {
showErrorDialog("Error parsing inventory entry: " + string(e.what()));
continue;
// Skip bad entry, continue with next
}
Needed because:
•
If any conversion fails, catches the error
•
Shows error message but doesn't crash program
• continue skips to next product
Cleanup and Validation:
// Close the file when done
if(inventory.empty()) { // Check if we got any products
file.close();
showWarningDialog("No valid products found in inventory file.");
}
Needed because:
•
Always close files when done
•
Tell user if no products were loaded successfully
File Opening Syntax:
ofstream file(INVENTORY_FILE, ios::out);
•
ofstream is a class for writing to files (output file stream)
•
ios::out is a mode flag telling how to open the file
•
Without ios::out, it might erase file contents first
Modern For Loop Syntax:
for(const Product& p : inventory)
•
This is a "range-based" for loop
•
const means read-only access
•
& is a reference (avoids copying)
•
: means "in" or "from"
•
Reads as "for each Product p in inventory"
Basic Writing Pattern:
file << p.productId << "|"
This means:
•
file << writes to our file
•
Takes what's in p.productId and writes it
•
Then writes a | symbol as a separator Like writing "1001|" to the file
2. String Fields:
<< p.name << "|"
<< p.supplier << "|"
<< p.location << "|"
These write text fields directly with | after each
3. Category Conversion:
<< categoryToString(p.category) << "|"
•
Remember category is stored as a number (0,1,2...)
•
categoryToString converts it to text ("Grocery", "Dairy"...)
•
Then writes that text to file
4. Number Formatting:
<< fixed << setprecision(2) << p.price << "|"
<< p.costPrice << "|"
•
fixed and setprecision(2) makes nice number format
•
2.5 becomes "2.50"
•
Each price gets a | after
5. Integer Values:
<< p.quantity << "|"
<< p.minStock << "|"
<< p.maxStock << "|"
•
Writes whole numbers directly
•
Each with | after
6. End of Line:
<< p.lastRestocked << endl;
•
Writes the last value
•
endl starts a new line for next product
Example Output:
1001|Coca Cola|Beverages|2.50|2.00|50|10|100|2024-12-31|Supplier1|ShelfA|1703345678
Try-Catch Structure:
try {
// code that might fail
} catch(const exception& e) {
// what to do if it fails
}
This is like a safety net - tries to do something, catches any errors that
happen
File Cleanup:
file.close();
•
Properly closes the file after writing
•
Like closing a notebook when you're done writing
Success Message:
showSuccessDialog("Inventory saved: " + to_string(inventory.size()) + "
items written.");
Let's break this down:
•
inventory.size() counts how many products we have
•
to_string() converts that number to text
•
+ combines the text parts So if we have 5 products, shows: "Inventory
saved: 5 items written."
Error Catching:
catch(const exception& e) {
showErrorDialog("Error saving inventory: " + string(e.what()));
}
If something goes wrong:
•
e is the error that happened
•
e.what() gets the error message
•
Shows error to user instead of crashing
For example, if file can't be saved
Error saving inventory: Permission denied
1. Initial Setup:
void addProduct() {
// Check where we're saving
Product p;
// Create new product
system("cls");
// Clear screen
drawSimpleBox("Add New Product"); // Show title
verifyFilePath();
2. Basic Information Input:
cout << "Product ID: ";
cin >> p.productId;
// Clear leftover newline
cin.ignore();
cout << "Name: ";
getline(cin, p.name); // Read full name with spaces
3. Category Selection:
do {
// Show menu
cout << "\nCategories:\n";
cout << "1. Grocery\n2. Dairy\n3. Meat\n4. Produce\n";
int catChoice;
// Declare variable to store user's menu choice
The input check:
if(cin >> catChoice && catChoice >= 1 && catChoice <= 7)
This line does multiple checks at once:
•
cin >> catChoice reads user input into catChoice
•
catChoice >= 1 checks if number is 1 or higher
•
catChoice <= 7 checks if number is 7 or lower
•
&& means ALL these conditions must be true
The conversion part:
p.category = static_cast<Category>(catChoice - 1);
This is new and important because:
•
User enters 1-7, but our enum starts at 0
•
catChoice - 1 converts user's choice to match enum
•
static_cast<Category> tells computer to treat the number as our
Category type
•
Example: User enters 1, becomes GROCERY (0) User enters 2, becomes
DAIRY (1)
The reason for this conversion:
•
Menu shows human-friendly numbers (1-7)
•
Computer needs enum values (0-6)
•
static_cast safely converts between these
// Invalid input handling
// Reset error flags
cin.ignore(1000, '\n'); // Clear bad input
} while(true); // Keep trying until valid input
cin.clear();
1. The do-while loop:
do {
// code
} while(true);
I used this because:
•
do means "do this first, then check condition"
•
while(true) means "keep doing it forever until we break"
•
This keeps asking for input until user enters valid price
The p.price syntax:
if(cin >> p.price && p.price > 0)
•
p is our Product object we created earlier with Product p;
•
.price accesses the price field of that product
•
p.price together means "the price field of this product"
3. The validation check:
if(cin >> p.price && p.price > 0)
This does two checks:
•
cin >> p.price tries to read a number into price field
•
p.price > 0 checks if that number is positive
•
If BOTH are true (valid number AND positive), then it's good
Error handling:
// Resets cin if user typed letters instead of numbers
cin.ignore(1000, '\n'); // Removes bad input so we can try again
cin.clear();
Getting Final Product Details:
cout << "Expiry Date (YYYY-MM-DD): ";
cin >> p.expiryDate;
cin.ignore();
// Clear newline after date input
•
Gets expiry date (like "2024-12-31")
•
cin.ignore() needed because we're switching from cin >> to getline
cout << "Supplier: ";
// Can read names with spaces
getline(cin, p.supplier);
•
Uses getline because supplier names might have spaces
•
Like "Walmart Store" or "Local Farms Inc"
p.lastRestocked = time(nullptr);
// Sets current time
•
time(nullptr) gets current computer time
•
Stores when we added this product
Saving the Product:
inventory.push_back(p);
// First save to memory
•
push_back adds to our vector list
•
Like adding a new page to a notebook
ofstream file(INVENTORY_FILE);
if(!file) {
// Open file for writing
// Check if file opened successfully
showErrorDialog("Unable to save!");
return;
}
•
Tries to open inventory.txt
•
Shows error if can't open file
The Try Block and Loop:
try {
for(const Product& product : inventory)
•
try starts a block that might have errors
•
const Product& means "look at but don't change each product"
•
: means "in" (loop through inventory)
•
& avoids copying each product (faster)
Writing Each Field With Formatting:
file << product.productId << "|"
<< product.name << "|"
// Write ID and separator
// Write name and separator
<< categoryToString(product.category) << "|"
to text
// Convert category number
Number Formatting:
<< fixed << setprecision(2) << product.price << "|" // Makes 2.5 into 2.50
<< product.costPrice << "|"
<< product.quantity << "|"
// Whole numbers don't need formatting
<< product.minStock << "|"
<< product.maxStock << "|"
Final Fields:
<< product.expiryDate << "|"
<< product.supplier << "|"
<< product.location << "|"
<< product.lastRestocked << endl;
// endl starts new line for next
product
Cleanup and Messages:
file.close();
// Close file when done
showSuccessDialog("Product added and saved to inventory successfully!");
Error Handling:
} catch(const exception& e) {
// If anything goes wrong
showErrorDialog("Error saving to file: " + string(e.what()));
// e.what() gets the error message
}
1. Function Setup:
void displayInventoryEnhanced() {
// Clear screen for fresh display
drawSimpleBox("Current Inventory"); // Draw title box
system("cls");
2. Setting Up Display Format:
vector<int> colWidths = {10, 20, 12, 10, 10, 15};
vector<string> headers = {"ID", "Name", "Category", "Price", "Stock", "Expiry"};
This defines how wide each column should be and what to label them. For
example:
•
ID column gets 10 spaces
•
Name column gets 20 spaces
•
Each width matches its corresponding header
drawSimpleTable(headers, colWidths);
This is a function call where we're passing two things:
•
First argument headers: our vector of column names ("ID", "Name", etc.)
•
Second argument colWidths: our vector of numbers (10, 20, 12, etc.)
The function then uses these to create your table header with proper spacing.
2. for(const Product& p : inventory)
for(const Product& p : inventory)
This is a range-based for loop with several important parts:
•
const: means we're promising not to change any products while looking
at them
•
Product&: is a reference to each product (like looking at it instead of
making a copy)
•
p: is the name we're giving to each product as we look at it
•
: inventory: means "look through everything in our inventory vector"
It's like saying "let me look at each product (p) in my inventory, one at a
time, without changing anything."
Traditional way would be:
for(int i = 0; i < inventory.size(); i++) {
Product p = inventory[i];
// work with p
}
cout << " " // Initial indent for alignment
// ID in 10 spaces
<< setw(20) << p.name
// Name in 20 spaces
<< setw(12) << categoryToString(p.category) // Convert category number to text
<< setw(10) << fixed << setprecision(2) << p.price // Price with 2 decimals
<< setw(10) << p.productId
// Stock amount
<< setw(15) << p.expiryDate << endl; // Expiry date
<< setw(10) << p.quantity
}
For each product, this creates a row where:
•
Each field is given exact space (setw)
•
Numbers are formatted nicely (fixed, setprecision)
•
Categories are converted from numbers to readable text
5. Bottom Border:
cout << string(4, ' ') << string(77, '=') << endl;
Creates a line of equal signs under the table:
•
4 spaces to match indentation
•
77 equal signs to match table width
The result looks like:
Current Inventory
---------------------------------ID
Name
Category
Price
Stock
Expiry
====================================================================
1001
Coca Cola
Beverages
2.99
50
2024-12-31
7757
Roshid Tears
Other
1.00
5
2030-12-20
====================================================================
1. Initial Setup and User Input:
Void updatestock() {
string productId;
// Create variable to store product ID
cout << "\nEnter Product ID to update: ";
cin >> productId;
// Get ID from user
2. Search Setup:
bool found = false;
// Flag to track if we find the product
We set this to false at start and will change it to true if we find the
product.
3. Searching Through Inventory:
for(Product& p : inventory) {
Important parts here:
// Look at each product
•
No const because we WILL change the product
•
& for reference because we need to modify the actual product
•
p represents each product as we look through inventory
4. Product Matching:
// If we find matching ID
// Mark that we found it
if(p.productId == productId) {
found = true;
cout << "Current quantity: " << p.quantity << endl;
// Show current
stock
5. Updating Quantity:
int newQuantity;
// Variable for new amount
cout << "Enter new quantity: ";
cin >> newQuantity;
// Get new amount from user
// Update the stock
p.lastRestocked = time(nullptr); // Record when we did this
p.quantity = newQuantity;
6. Saving and Feedback:
saveInventory();
// Save changes to file
// Show we're working
showSuccessDialog("Stock updated successfully!"); // Confirm success
return; // Exit function after successful update
showLoadingAnimation("Updating stock");
7. Error Handling:
if(!found) {
// If we looked through everything and didn't find the product
showErrorDialog("Product not found!");
return;
// Exit function if product not found
}
1. Function Introduction and Setup:
void checkLowStock() {
cout << "\nLow Stock Alert\n";
cout << "===============\n";
bool found = false;
// Start with assuming no low stock
We start by creating a header for our alert and set up a 'found' flag. Think
of this flag like a marker - it starts at false and will change to true if
we find any products running low.
2. Checking Every Product:
for(const Product& p : inventory) {
This loop goes through every product in our inventory. We use const because
we're just looking at products, not changing them. The & means we're looking
at the original products, not making copies (more efficient).
3. The Low Stock Check:
if(p.quantity <= p.minStock) {
cout << p.name << " - Current stock: " << p.quantity
<< " (Minimum: " << p.minStock << ")\n";
found = true;
}
For each product, we check if its current quantity is at or below its minimum
stock level. If it is:
•
Show the product name
•
Show how many we have (current stock)
•
Show how many we should have (minimum stock)
•
Mark that we found at least one low stock item (found = true)
4. Final Results:
if(!found) {
cout << "No products are low in stock.\n";
}
After checking everything, if we never found any low stock (found is still
false)
updateStockForSale function is separate from but related to the checkLowStock
function
above.
While
checkLowStock
just
checks
inventory
levels,
function actually updates stock when making a sale.
Let me break down this function completely:
bool updateStockForSale(const string& productId, int quantity)
First, notice the function:
•
Returns bool (true/false) to tell if the sale was successful
•
Takes two parameters:
o const string& productId: which product to sell
o int quantity: how many to sell
this
for(Product& p : inventory) {
if(p.productId == productId) {
This searches inventory for the product:
•
Uses & because we need to change the quantity
•
No const because we'll modify the product
•
Checks if we found the right product ID
if(p.quantity >= quantity) {
p.quantity -= quantity;
// Subtract sold amount
This is the key validation:
•
Checks if we have enough in stock
•
If yes, reduces the quantity by amount sold
•
If no, will return false (can't sell what we don't have)
saveInventory();
// Save changes to file
cout << "Logging transaction for ProductID: " << productId << endl;
logTransaction(productId, quantity, true);
return true;
// Sale successful
If sale is possible:
•
Saves updated inventory to file
•
Logs the sale in transactions
•
Returns true to indicate success
return false;
// Return false if product found but not enough stock
The first return false happens if:
•
We found the product
•
But don't have enough quantity
return false;
// Return false if product not found at all
The final return false happens if:
•
We searched all inventory
•
Never found the product ID
Function Declaration:
void logTransaction(const string& productId, int quantity, bool isSale)
This function takes three pieces of information:
•
productId: Which product was sold/restocked (using reference to save
memory)
•
quantity: How many items were involved
•
isSale: True if we're selling, False if we're restocking
2. Opening File in Append Mode:
ofstream file(TRANSACTIONS_FILE, ios::app);
// Append mode
Important parts:
•
ofstream: For writing to files
•
ios::app: Means "append" - add to end of file instead of overwriting
•
This way, we keep a history of all transactions
3. File Open Check:
if(!file) {
showErrorDialog("Failed to open transactions file!");
return;
}
Safety check to make sure file opened correctly.
4. Writing Transaction with Timestamp:
try {
time_t now = time(nullptr);
// Get current time
file << put_time(localtime(&now), "%Y-%m-%d %H:%M:%S") << "|" // Format time
<< productId << "|"
<< quantity << "|"
<< (isSale ? "SALE" : "RESTOCK") << endl;
Let's break this down:
•
Gets current time
•
Formats it like "2024-12-23 14:30:45"
•
Uses | to separate fields
•
Uses ternary operator (?:) to write "SALE" or "RESTOCK"
5. Ensuring Data is Saved:
file.flush();
file.close();
// Force write to disk immediately
// Close file properly
These ensure our transaction is actually saved.
6. Confirmation and Error Handling:
cout << "Transaction logged: " << productId << " | " << quantity << endl;
} catch(const exception& e) {
showErrorDialog("Error logging transaction: " + string(e.what()));
}
Example of what gets written to transactions.txt:
2024-12-23 14:30:45|1001|5|SALE
2024-12-23 14:35:12|2002|10|RESTOCK
1. Getting Product Price
double getProductPrice(const string& productId) {
for(const Product& p : inventory) {
if(p.productId == productId) {
return p.price;
}
}
return -1;
// Return -1 if product not found
}
This function:
•
Takes a product ID and looks up its price
•
Uses const because we're just reading, not changing anything
•
Returns -1 as a special value meaning "product not found"
•
Used when calculating bills or checking prices
2. Checking If Inventory Has Products
bool hasInventory() {
return !inventory.empty();
}
This is a simple but important function that:
•
Checks if our inventory vector has any products at all
•
Returns true if we have at least one product
•
Returns false if inventory is empty
•
The ! before inventory.empty() flips the result (empty becomes false,
not empty becomes true)
3. Getting Product Name
string getProductName(const string& productId) {
for(const Product& p : inventory) {
if(p.productId == productId) {
return p.name;
}
}
return "";
// Return empty string if product not found
}
Similar to getProductPrice, but:
•
Returns the product's name instead of price
•
Returns an empty string ("") if product isn't found
•
Used when displaying products in bills or reports
These functions work together in your billing system. For example, when
showing a bill:
string id = "1001";
// First check if we have any products
string name = getProductName(id); // Get product name
double price = getProductPrice(id); // Get its price
// Use these to show the bill
if(hasInventory()) {
}
1. Class Structure and Private Members:
class BillingSystem {
private:
// Reference to inventory system
vector<pair<string, int>> currentBill; // Stores product IDs and quantities
bool hasBillCreated = false;
// Tracks if a bill exists
InventoryManager& inventoryManager;
The private members are crucial because:
•
inventoryManager is a reference (&) because we need to work with the
actual inventory, not a copy
•
currentBill uses pair<string, int> to store two pieces of information for
each item:
o string: the product ID
o int: how many of that product
•
hasBillCreated tracks if we've started a bill (prevents adding items
without a bill)
2. Constructor:
public:
BillingSystem(InventoryManager& im) : inventoryManager(im) {}
This initializes the billing system by:
•
Taking a reference to an existing InventoryManager
•
Using constructor initialization list (: inventoryManager(im))
•
Empty {} because no other initialization needed
3. Bill Status Check:
bool hasBillItems() const {
return !currentBill.empty();
}
This function:
•
Returns true if bill has items
•
Uses const because it doesn't change anything
•
!currentBill.empty() flips the empty check to true if we have items
4. Creating a New Bill:
void createNewBill() {
currentBill.clear();
// Remove any old items
Setting the Bill Status
hasBillCreated = true;
This is like turning on a light switch - it tells our system "yes, we now
have an active bill." We need this flag because:
•
We can't add items without a bill
•
We need to know if a bill exists for other operations
•
It prevents confusion about bill state
Getting and Formatting Time
// Get current time
string timestamp = ctime(&now); // Convert to readable format
time_t now = time(nullptr);
Let's break this down:
•
time_t is a special type that stores seconds since January 1, 1970
•
time(nullptr) gets the current time from your computer
•
ctime(&now) converts that number into something readable like "Mon Dec
23 14:30:22 2024"
Creating a Unique Bill ID
cout << "Bill ID: BILL-" << put_time(localtime(&now), "%Y%m%d%H%M%S") <<
endl;
This creates a unique identifier for each bill:
•
localtime(&now) converts computer time to local time
•
put_time formats it using special codes:
o %Y = year (2024)
o %m = month (12)
o %d = day (23)
o %H = hour (14)
o %M = minute (30)
o %S = second (22)
•
Result looks like: "BILL-20241223143022"
offers to add items:
char addItemChoice;
cout << "Would you like to add items now? (Y/N): ";
cin >> addItemChoice;
if(toupper(addItemChoice) == 'Y') {
addToBill();
// Convert to uppercase for checking
// Start adding items
}
what this function does - it's like a cashier adding items to your bill at
a store. Let me break down each part:
if(!hasBillCreated) {
showErrorDialog("Please create a new bill first (Option 5)!");
return;
}
This is our safety check. Just like a cashier can't add items without opening
a new transaction, we check if a bill exists. If not, we show an error and
stop.
string productId;
int quantity;
inventoryManager.displayInventoryEnhanced();
This part prepares for adding items. We create variables to store what
product and how many, then show the available inventory - like a cashier
looking at what's in stock.
cout << "\nEnter Product ID: ";
cin >> productId;
cout << "Enter Quantity: ";
cin >> quantity;
This is where we get information from the user, similar to scanning items at
a checkout.
The most important part is the stock check and update:
if(inventoryManager.updateStockForSale(productId, quantity))
•
inventoryManager is our object that manages inventory
•
.updateStockForSale is a method we call on that object
•
(productId, quantity) are the parameters we pass to the method
•
The whole thing returns true or false, which the if statement checks
currentBill.push_back({productId, quantity});
•
currentBill is our vector of pairs that stores bill items
•
.push_back() is a vector method that adds an item to the end
•
{productId, quantity} creates a pair right there in the code (called an
initializer list)
•
The curly braces {} are creating a temporary pair object
The syntax here is showing several C++ concepts working together:
1. Method chaining (calling updateStockForSale on inventoryManager)
2. Conditional execution (if statement)
3. Vector operations (push_back)
4. Pair creation using initializer list syntax
This is different from older C++ where you might write:
pair<string, int> newItem(productId, quantity);
currentBill.push_back(newItem);
The modern syntax {productId, quantity} is more concise but does the same
thing - creates a pair where:
•
First element (productId) is the string identifying the product
•
Second element (quantity) is the integer amount being purchased
Initial Check:
if(!hasBillItems()) {
showErrorDialog("No items in current bill!");
return;
}
•
! negates the result of hasBillItems()
•
Empty bill check happens first to fail fast
•
return exits function early if no items
2. Table Setup:
vector<int> colWidths = {25, 10, 10, 15};
vector<string> headers = {"Product", "Quantity", "Price", "Total"};
•
Using vectors allows flexible column management
•
Initializer lists ({}) provide clean, readable way to set values
•
Numbers match header text lengths plus padding
Setting Up the Total and Loop
double total = 0;
// Start with zero total
for(const auto& item : currentBill) {
•
const means we won't change the items we're looking at
•
auto tells C++ to figure out what type of data we're working with
•
& means we're looking at the original items, not making copies
•
: currentBill means "look through everything in our bill"
2. Getting the Price
double price = inventoryManager.getProductPrice(item.first);
Remember how currentBill stores pairs? Each item is a pair of:
•
item.first: the product ID (like "1001")
•
item.second: the quantity (like 3)
So inventoryManager.getProductPrice(item.first) means:
•
Take the product ID (item.first)
•
Ask our inventory manager "what's the price of this item?"
•
Store that price in our price variable
It's like a cashier looking up how much each item costs.
3. Calculating Item Total
double itemTotal = price * item.second;
total += itemTotal;
This is basic multiplication but important:
•
price is how much one item costs (like $2.99)
•
item.second is how many they're buying (like 3)
•
itemTotal becomes the total for this item ($2.99 * 3 = $8.97)
•
total += itemTotal adds this to our running total
4. Displaying the Item
cout << "
" << setw(25) << inventoryManager.getProductName(item.first)
This displays each item:
•
" " adds spaces at the start for indentation
•
setw(25) makes a space 25 characters wide
•
inventoryManager.getProductName(item.first) gets the product name using the
ID
For example, if item.first is "1001", this might display:
Coca Cola
// Name in a 25-character wide space
<< setw(10) << item.second
Quantity in 10 spaces
//
<< setw(10) << fixed << setprecision(2) << price
//
Price with 2 decimals
//
<< setw(15) << itemTotal << endl;
Total with wider space
Finally, the total line:
cout << string(4, ' ') << string(60, '=') << endl;
// Draw line using 60 equals signs
cout << "
" << setw(45) << "Total Amount: $"
// Right-align total amount
<< setw(15) << fixed << setprecision(2) << total << endl;
// Show total with 2 decimals
void saveCurrentBill() {
inventoryManager.saveInventory();
}
•
inventoryManager is our reference to the InventoryManager class
•
.saveInventory() is calling its method
•
We need this because when we finish a bill, the inventory numbers have
changed and need saving
void clearBill() {
if(!hasBillItems()) {
showWarningDialog("Bill is already empty!");
return;
}
currentBill.clear();
showSuccessDialog("Bill cleared successfully!");
}
This connects to our bill tracking:
•
Uses hasBillItems() we defined earlier to check bill state
•
currentBill.clear() empties our vector of items
•
Shows appropriate messages based on the action
The EXIT_MESSAGE uses a special syntax:
const string EXIT_MESSAGE = R"(
...
)";
•
R"(" is a "raw string literal" in C++
•
It lets us write multiple lines with formatting exactly as we want
•
We don't need to use \n for new lines
•
Used here because we want a nicely formatted goodbye message
Then our menu validation:
bool isValidMenuChoice(int choice) {
return choice >= 1 && choice <= 9;
}
This connects to our menu system:
•
Takes the user's input number
•
Checks if it's within our menu range (1-9)
•
Used by the main menu to validate choices
getValidatedMenuChoice() Function
int getValidatedMenuChoice() {
// Variable to store user's menu selection
int choice;
This line creates a space in memory to store the number (1-9) that the user will input.
while(true) {
// Infinite loop until valid input received
cout << " Enter your choice (1-9): ";
We use an infinite loop because we want to keep asking until we get a valid input. This is better than letting
the program continue with an invalid choice.
if(cin >> choice && isValidMenuChoice(choice)) {
return choice;
}
This line does two checks at once:
•
•
cin >> choice tries to read a number (returns false if user types letters)
isValidMenuChoice(choice) checks if the number is between 1-9 If both succeed, we return the
valid choice.
// Reset error flags if input failed
cin.ignore(1000, '\n'); // Clear bad input from buffer
cin.clear();
These lines handle invalid input by:
•
•
clear() resetting the input system if it failed (like if user typed letters)
ignore() removing up to 1000 characters until it finds a newline This prevents the system from
getting stuck in a bad state.
showMainMenu() Function
int showMainMenu() {
// Draw the menu options
return getValidatedMenuChoice(); // Get and return valid choice
drawSimpleFrame();
}
This function combines two operations:
1. Shows the menu interface (using drawSimpleFrame)
2. Gets a valid choice from the user
Lastly, break down the main function, which is the heart of your program:
1. Initial Setup SyntaxSupermarketSystem system;
// Create main system object
InventoryManager im;
// Create inventory manager
BillingSystem bs(im);
// Create billing system, connected to inventory
bool exit = false;
// Control program loop
Written this way because:
- Need these objects throughout program's life
- BillingSystem needs InventoryManager reference
- exit flag controls main program loop
2. Main Program Loopwhile(!exit) {
int choice = showMainMenu();
switch(choice) {
Using while-switch because:
- Continuous operation until exit
- switch is cleaner than multiple if-else for menu options
- Each case handles one menu option
3. Menu Options Pattern:
Each case follows this pattern:
case 1:
system.clearScreen();
// Clear display
showConfirmDialog("add a new product..."); // Ask for confirmation
char addChoice;
cin >> addChoice;
if(toupper(addChoice) == 'Y') {
im.addProduct();
// Convert to uppercase for checking
// Perform action
showSuccessDialog("Product added..."); // Show result
}
break;
This structure ensures:
- Screen stays clean
- User confirms important actions
- Appropriate feedback shown
4. Error Handling Pattern:
if(im.hasInventory()) {
im.displayInventoryEnhanced();
} else {
showErrorDialog("No products in inventory!");
}
Used throughout because:
- Checks conditions before actions
- Shows clear error messages
- Prevents invalid operations
5. Program Exitcase 9:
if(toupper(exitChoice) == 'Y') {
im.saveInventory();
// Save data
showLoadingAnimation("Saving data");
Sleep(1500);
exit = true;
// Pause to show messages
// End program loop
}
How these connect:
User Input → Menu Selection → Action Confirmation → Operation → Feedback → Loop
Download