#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