Compare commits

...

19 Commits

Author SHA1 Message Date
joelthomastrenser 67e5917a57 Fix: improve complete payments table formatting and payment mode validation
- Increased spacing between invoice table columns for better readability
- Updated invoice table headers with clearer labels
- Prevented screen clear before invoice listing display
- Changed payment mode selection to re-prompt on invalid input instead of defaulting to offline mode
- Removed unnecessary blank line before service listing display

Fixes #1786
2026-05-27 13:02:40 +05:30
joelthomastrenser 83e2bed432 Fix: improve notification display formatting and add global exception handling
- add truncateString utility for long notification titles
- improve notification table alignment
- remove unnecessary heading spacing in notification preferences
- add application-level exception handling in UserInterface::run()

Fixes #1777
2026-05-27 12:19:34 +05:30
joelthomastrenser a9c8ec93b7 Fix: detach removed users from service observers
- detach users from InventoryManagementService observers during removal
- detach users from PaymentManagementService observers during removal
- detach users from ServiceManagementService observers during removal

Fixes #1783
2026-05-27 11:10:59 +05:30
Jissin Mathew 13b7d6d40a Merged PR 1114: Fix Admin Menu, Service Management, Technician Assignment, and Invoice Display Issues
**Changes:**

- Fixed booking status flow (PENDING → STARTED) during job card creation
- Updated purchaseService to initialize bookings with PENDING status
- Filtered active users and services before listing/selection
- Improved Assign Job flow: pending bookings only, clearer technician prompts, consistent feedback
- Enhanced View Invoices: reference parameter, empty items handling, improved formatting
- Added filterActiveServices helper for consistent active service filtering
- Fixed Remove Service to also deactivate dependent combo packages
- Improved Create Combo Packages: cancel option, confirmation step, duplicate/edge case handling
- Updated Remove Combo Package to handle empty states gracefully without exceptions

Related work items: #1646, #1740, #1744, #1745, #1747, #1749, #1752
2026-05-26 21:26:37 +05:30
joelthomastrenser 665e83d56a Implement review fixes 2026-05-26 21:25:02 +05:30
Jissin Mathew 8268b90d82 Fix Service Removal and Combo Package Management Issues
- Added ComboPackage dependency check in removeService; deactivates related packages when a service is removed.
- Added filterActiveServices helper to list only active services.
- Updated removeService and removeComboPackage with headings, empty-state checks, and consistent success/error messages.
- Enhanced createComboPackages:
  - Uses active services only.
  - Added cancel option and confirmation after first selection.
  - Prevented duplicate selection and infinite loop when limited services exist.
  - Improved feedback when all available services are selected.
- Updated selectServicesToRemove and selectComboPackage to handle empty states gracefully without exceptions.

Fixes #1749
2026-05-26 21:13:40 +05:30
Jissin Mathew 7646ce6644 Fix Assign Job to Technician issues and View invoices issues
- Assign Job to Technician:
  - Added heading for clearer user guidance.
  - Filtered only pending service bookings for assignment.
  - Improved technician listing and selection with clearer prompts.
  - Ensured booking status transitions correctly from PENDING to STARTED when job cards are created.
  - Enhanced feedback messages for technician availability and job card creation.

- View Invoices:
  - Added heading "View Invoices" for better UI consistency.
  - Updated displayInvoices to take map by reference for efficiency.
  - Improved formatting of invoice details with consistent spacing and line breaks.
  - Added handling for empty invoice parts list (shows "No inventory items used").
  - Enhanced error messages when encountering null invoices.

Fixes #1745
Fixes #1752
2026-05-26 21:13:13 +05:30
Jissin Mathew 05499e4890 Fix Create Service Submenu
- Added heading "Create Service" to submenu for clearer user guidance.
- Ensured console clears before service creation for consistent UI behavior.
- Filtered active inventory items before selection to avoid showing inactive items.
- Updated prompts with improved formatting and spacing (e.g., labour cost input, success message).
- Refactored selectInventoryItems:
  - Added "Select Required Items" heading for clarity.
  - Skipped null or inactive items during listing.
  - Improved empty inventory message ("No Items Present, Inventory empty").
  - Enhanced success and error messages with consistent line breaks.
  - Simplified logic for item selection and exit handling.

Fixes #1747
2026-05-26 21:12:17 +05:30
Jissin Mathew 3b82648e45 Fix Add Technician submenu
- Updated AdminMenu::addTechnician:
  - Added heading "Add Technician" for better user guidance.
  - Removed setw formatting, replaced with simpler prompts.
  - Ensured console clears before operation for consistency.
  - Improved error messages for password, email, and phone validation with line breaks for readability.
  - Enhanced success message with consistent spacing.
- Minor cleanup in TechnicianMenu.cpp by adding a blank line after file header for formatting consistency.

Fixed #1744
2026-05-26 21:12:16 +05:30
Jissin Mathew 937ba2e7cf Fix Add Inventory Item Issues
- Added heading "Add Inventory Item" to submenu for clearer user guidance.
- Updated option text from "Add Quantity" to "Restock Item".
- Ensured console clearing (util::clear) before add item and restock operations.
- Reformatted prompts with consistent spacing and line breaks (e.g., "Enter Item Details", "Select Item to Restock").
- Added default case handling in AdminMenu for invalid choices.
- Enhanced MenuHelper output:
  - Extra line breaks for readability.
  - Improved error messages ("Invalid index selected", "No active items available").
  - Clearer success message when updating stock with new quantity.

Fixes #1740
2026-05-26 21:12:16 +05:30
Avinash Rajesh f63e4056f2 Merged PR 1110: Fix user management, customer menu, inventory, notification, and validation issues
Changes:
- Added validation in AdminMenu::viewStockLevels and improved stock level formatting with headers, column widths, and pressEnter prompts
- Enhanced AdminMenu::checkStockAvailability with header, clear screen, and missing item validation
- Updated AdminMenu::removeUser with header and excluded ADMIN users from active list
- Improved CustomerMenu::completePayments with pending invoice checks, error handling, and clearer headers
- Refined selectInvoiceFromUserForPayment in MenuHelper.h (inline, clear screen, updated headers)
- Added "View Service History" header in CustomerMenu::viewServiceHistory and widened table columns
- Improved notification handling: added headers, empty list validation, pressEnter prompts, and adjusted column widths
- Simplified notification titles in Inventory, Payment, and Service Management services
- Strengthened UserManagementService::updateUserDetails with duplicate checks (email, phone), clearer errors, and Validator integration
- Implemented new duplicate validation functions (isUsernameDuplicate, isPhoneDuplicate, isEmailDuplicate) in Validator utilities
- Updated CustomerMenu::updateDetails with header, improved error/success message formatting, and validation integration

Related work items: #1646, #1739, #1741, #1742, #1746, #1748, #1750
2026-05-26 21:05:38 +05:30
joelthomastrenser febfa45e4a Implement review fixes
Changes:

 - Strengthened UserManagementService::updateUserDetails by checking duplicates only when email/phone are changed, preventing false errors
 - Updated AdminMenu::viewStockLevels header text from "View Stock Level" to "View Stock Levels" for consistency
 - Cleaned up CustomerMenu::updateDetails by removing unused user list retrieval and improving header/message formatting
2026-05-26 21:03:22 +05:30
Avinash Rajesh 33cbb1dac3 Fix Update Profile and User Validation Issues
- Included Validator.h in UserManagementService.cpp for duplicate checks.
- Enhanced updateUserDetails in UserManagementService to validate:
  - Throw error if user does not exist.
  - Throw error if email already exists among active users.
  - Throw error if phone number already exists among active users.
- Implemented new duplicate validation functions in Validator.cpp:
  - isUsernameDuplicate
  - isPhoneDuplicate
  - isEmailDuplicate
- Declared new duplicate validation functions in Validator.h.
- Updated CustomerMenu::updateDetails:
  - Added "Update Details" header for clarity.
  - Improved error messages with newline formatting.
  - Added success message with newline formatting.

Fixes #1746
2026-05-26 20:24:36 +05:30
Avinash Rajesh 80b91f3f1b Fix View Service History and View Notification Issue
- Updated sendNotification in InventoryManagementService, PaymentManagementService, and ServiceManagementService to use only the provided title instead of prefixing with service name.
- Added "View Service History" header in CustomerMenu::viewServiceHistory for clarity.
- Adjusted column widths in service history table for better alignment:
  - Booking ID column widened to 15.
  - Vehicle Brand, Vehicle Number, Vehicle Model, Discount %, and Status columns widened to 20.
- Updated output formatting in CustomerMenu::viewServiceHistory to match new widths.
- Added "View and Delete Notification" header in MenuHelper::viewAndDeleteNotification for clarity.
- Moved empty notification validation from selectNotification to viewAndDeleteNotification:
  - Displays "No notifications available." message.
  - Added util::pressEnter() prompt before returning when no notifications exist.
- Increased Notification title column width from 30 to 35 in selectNotification for improved readability.

Fixes #1748
2026-05-26 20:23:18 +05:30
Avinash Rajesh a87af89a8a Fix Complete Payment Issue
- Added "Complete Payments" header in CustomerMenu::completePayments for clarity.
- Implemented validation to check if invoices list is empty and display appropriate message.
- Added logic to verify presence of pending invoices before proceeding with payment.
- Enhanced error handling to show "Payment failed" with pressEnter prompt when no invoice is selected.
- Updated selectInvoiceFromUserForPayment in MenuHelper.h:
  - Changed function to inline and added util::clear() at start.
  - Updated column headers to "Technician ID" and "Technician Name" for clarity.
- Removed temporary AdminMenu.cpp file from enc_temp_folder.

Fixes #1750
2026-05-26 20:23:16 +05:30
Avinash Rajesh f1ca8c2a58 Fix Remove User Issue
- Added "Remove User" header in AdminMenu::removeUser for clarity.
- Updated filterActiveUsers in MenuHelper.h to exclude ADMIN users from the active user list.

Fixes #1742
2026-05-26 20:23:16 +05:30
Avinash Rajesh d55bbb6349 Fix Check stock availability Issue and Add error message
- Added "Check Stock Availability" header in AdminMenu::checkStockAvailability for clarity.
- Updated prompt text to "Enter the Item ID" for consistency.
- Added util::clear() after reading Item ID to refresh the screen before displaying results.
- Implemented validation to show "Item not Found" message when the entered ID does not exist in inventory.
Fix #1741
2026-05-26 20:23:15 +05:30
Avinash Rajesh ae488f5670 Fix View Stock Level and improve error messages
- Added validation in AdminMenu::viewStockLevels to check for empty inventory before displaying.
- Added validation in AdminMenu::viewStockLevels to ensure only active items are shown.
- Added "View Stock Level" header for clarity.
- Adjusted column widths for Quantity and Price to 15 for better alignment.
- Added final newline and util::pressEnter() prompt after displaying stock levels.
Fixes  #1739
2026-05-26 20:23:15 +05:30
joelthomastrenser d8f7e46180 Merged PR 1108: Fix authentication, registration, customer menu, utility, and structural issues
Changes:

- Fixed login error handling and authentication validation issues
- Added duplicate checks during customer registration
- Fixed missing headers and message formatting issues in Login/Register screens
- Improved common utility and console interaction behavior
- Fixed Select Individual Service issues (booking status and empty service handling)
- Fixed Select Combo Package issues (header and empty combo package handling)
- Fixed low stock alert loop condition bug
- Updated MenuHelper.h functions from static to inline

Related work items: #1646, #1736, #1737, #1738, #1743, #1751, #1753, #1754
2026-05-26 20:14:40 +05:30
11 changed files with 554 additions and 287 deletions
@@ -323,7 +323,7 @@ void InventoryManagementService::sendNotification(User* user, const std::string&
Factory::getObject<Notification>(
user->getId(),
user,
"InventoryManagementService: " + title,
title,
message,
util::Timestamp()
);
@@ -86,7 +86,7 @@ void PaymentManagementService::sendNotification(User* user, const std::string& t
Factory::getObject<Notification>(
user->getId(),
user,
"PaymentManagementService: " + title,
title,
message,
util::Timestamp()
);
@@ -23,10 +23,10 @@ Date:19-May-2026
#include "ServiceBooking.h"
#include "ServiceManagementService.h"
#include "Timestamp.h"
#include "Timestamp.h"
#include "User.h"
#include "UserManagementService.h"
#include "Utility.h"
#include "ComboPackage.h"
/*
Function: purchaseService
@@ -175,7 +175,7 @@ void ServiceManagementService::sendNotification(User* user, const std::string& t
Factory::getObject<Notification>(
user->getId(),
user,
"ServiceManagementService: " + title,
title,
message,
util::Timestamp()
);
@@ -817,6 +817,10 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const
}
currentBooking->setAssignedTechnician(selectedTechnician);
currentBooking->setAssignedTechnicianId(selectedTechnician->getId());
if (currentBooking->getStatus() == util::ServiceJobStatus::PENDING)
{
currentBooking->setStatus(util::ServiceJobStatus::STARTED);
}
std::string title = "Job card created";
std::string message = "Job card created for the service and you are assigned for that.";
JobCard* jobCard = Factory::getObject<JobCard>(bookingID, currentBooking, currentService, serviceID, technicianID, selectedTechnician, util::Timestamp(), util::ServiceJobStatus::STARTED, util::Timestamp());
@@ -906,9 +910,27 @@ Throws:
void ServiceManagementService::removeService(const std::string& serviceID)
{
util::Map<std::string, Service*>& currentServices = m_dataStore.getServices();
util::Map<std::string, ComboPackage*>& currentComboPackages = m_dataStore.getComboPackages();
if (currentServices.find(serviceID) != -1)
{
currentServices.getValueAt(currentServices.find(serviceID))->setState(util::State::INACTIVE);
for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++)
{
ComboPackage* currentComboPackage = currentComboPackages.getValueAt(iterator);
if (currentComboPackage && currentComboPackage->getState() == util::State::ACTIVE)
{
util::Map<std::string, Service*> currentServices = currentComboPackage->getServices();
for (int iterator = 0; iterator < currentServices.getSize(); iterator++)
{
auto currentService = currentServices.getValueAt(iterator);
if (currentService->getId() == serviceID)
{
currentComboPackage->setState(util::State::INACTIVE);
break;
}
}
}
}
}
else
{
@@ -111,9 +111,23 @@ void UserManagementService::updateUserDetails(const std::string& userID, const s
int index = usersMap.find(userID);
if (index == -1)
{
throw std::runtime_error("User does not exist!");
throw std::runtime_error("User does not exist!\n");
}
User* user = usersMap.getValueAt(index);
if (email != user->getEmail())
{
if (util::isEmailDuplicate(email, usersMap))
{
throw std::runtime_error("Email already exists!\n");
}
}
if (phone != user->getPhone())
{
if (util::isPhoneDuplicate(phone, usersMap))
{
throw std::runtime_error("Phone number already exists!\n");
}
}
user->setEmail(email);
user->setPhone(phone);
}
@@ -283,6 +297,9 @@ Return type: void
*/
void UserManagementService::removeUser(const std::string& userID)
{
InventoryManagementService inventoryManagementService;
PaymentManagementService paymentManagementService;
ServiceManagementService serviceManagementService;
int index = m_dataStore.getUsers().find(userID);
if (index != -1)
{
@@ -290,6 +307,9 @@ void UserManagementService::removeUser(const std::string& userID)
if (user != nullptr)
{
user->setState(util::State::INACTIVE);
inventoryManagementService.detach(user);
paymentManagementService.detach(user);
serviceManagementService.detach(user);
}
}
}
@@ -301,7 +321,7 @@ util::Map<std::string, User*> UserManagementService::getUsers(util::UserType typ
for (int iterator = 0; iterator < currentUsers.getSize(); iterator++)
{
User* currentUser = currentUsers.getValueAt(iterator);
if (currentUser->getUserType() == type)
if (currentUser && currentUser->getState() == util::State::ACTIVE && currentUser->getUserType() == type)
{
filteredUsersMap.insert(currentUser->getId(), currentUser);
}
@@ -21,4 +21,28 @@ namespace util
{
std::cout << "\x1B[2J\x1B[H" << std::flush;
}
/*
Function: truncateString
Description:
Truncates a string if its length exceeds the given maximum length.
The truncated string ends with "..." to indicate omitted characters.
Parameters:
- text: const std::string&, input string to truncate
- maxLength: size_t, maximum allowed length of the returned string
Returns:
- std::string: Original string if within limit, otherwise truncated string with "..."
*/
inline std::string truncateString(const std::string& text, size_t maxLength)
{
if (text.length() <= maxLength)
{
return text;
}
if (maxLength <= 3)
{
return std::string(maxLength, '.');
}
return text.substr(0, maxLength - 3) + "...";
}
}
@@ -108,6 +108,17 @@ bool util::isPasswordValid(const std::string& password)
return hasUpper && hasLower && hasDigit && hasSpecial;
}
/*
* Function: isUsernameDuplicate
* Description: Checks if the given username already exists among active users.
* Parameters:
* username - string containing the username to validate
* usersMap - map of user objects keyed by identifier
* Returns:
* bool - true if the username is already in use by an active user, false otherwise
* Notes:
* - Only considers users with state util::State::ACTIVE
*/
bool util::isUsernameDuplicate(const std::string& username, const util::Map<std::string, User*>& usersMap)
{
int index = usersMap.findIf(
@@ -119,6 +130,17 @@ bool util::isUsernameDuplicate(const std::string& username, const util::Map<std:
return index != -1;
}
/*
* Function: isPhoneDuplicate
* Description: Checks if the given phone number already exists among active users.
* Parameters:
* phone - string containing the phone number to validate
* usersMap - map of user objects keyed by identifier
* Returns:
* bool - true if the phone number is already in use by an active user, false otherwise
* Notes:
* - Only considers users with state util::State::ACTIVE
*/
bool util::isPhoneDuplicate(const std::string& phone, const util::Map<std::string, User*>& usersMap)
{
int index = usersMap.findIf(
@@ -130,6 +152,17 @@ bool util::isPhoneDuplicate(const std::string& phone, const util::Map<std::strin
return index != -1;
}
/*
* Function: isEmailDuplicate
* Description: Checks if the given email address already exists among active users.
* Parameters:
* email - string containing the email address to validate
* usersMap - map of user objects keyed by identifier
* Returns:
* bool - true if the email address is already in use by an active user, false otherwise
* Notes:
* - Only considers users with state util::State::ACTIVE
*/
bool util::isEmailDuplicate(const std::string& email, const util::Map<std::string, User*>& usersMap)
{
int index = usersMap.findIf(
@@ -139,4 +172,4 @@ bool util::isEmailDuplicate(const std::string& email, const util::Map<std::strin
}
);
return index != -1;
}
}
@@ -158,10 +158,33 @@ void AdminMenu::viewStockLevels()
{
util::clear();
auto inventoryItems = m_controller.getInventoryItems();
bool hasActiveItems = false;
std::cout << "View Stock Levels" << std::endl;
if (inventoryItems.isEmpty())
{
std::cout << "No items found in Inventory.\n";
util::pressEnter();
return;
}
for (int index = 0; index < inventoryItems.getSize(); index++)
{
const InventoryItem* item = inventoryItems.getValueAt(index);
if (item->getState() == util::State::ACTIVE)
{
hasActiveItems = true;
break;
}
}
if (!hasActiveItems)
{
std::cout << "No active Inventory Item found.\n";
util::pressEnter();
return;
}
std::cout << std::left << std::setw(15) << "Item ID"
<< std::setw(25) << "Part Name"
<< std::setw(10) << "Quantity"
<< std::setw(10) << "Price"
<< std::setw(15) << "Quantity"
<< std::setw(15) << "Price"
<< std::endl;
for (int iterator = 0; iterator < inventoryItems.getSize(); ++iterator)
{
@@ -172,12 +195,14 @@ void AdminMenu::viewStockLevels()
{
std::cout << std::left << std::setw(15) << item->getId()
<< std::setw(25) << item->getPartName()
<< std::setw(10) << item->getQuantity()
<< std::setw(10) << item->getPrice()
<< std::setw(15) << item->getQuantity()
<< std::setw(15) << item->getPrice()
<< std::endl;
}
}
}
std::cout << "\n";
util::pressEnter();
}
/*
@@ -193,13 +218,15 @@ void AdminMenu::addInventoryItem()
int choice, quantity;
double price;
std::string partName;
std::cout << "1. Add new item \n2. Add Quantity\nEnter your choice : ";
std::cout << "Add Inventory Item\n";
std::cout << "1. Add new item \n2. Restock Item\n\nEnter your choice : ";
util::read(choice);
switch (choice)
{
case 1:
case 1:
{
std::cout << "--------Enter Item Details----------\n";
util::clear();
std::cout << "Enter Item Details\n";
std::cout << "Part Name : ";
util::read(partName);
std::cout << "Quantity : ";
@@ -207,15 +234,21 @@ void AdminMenu::addInventoryItem()
std::cout << "Price : ";
util::read(price);
m_controller.addInventoryItem(partName, quantity, price);
std::cout << "New Item " << partName << " added to the Inventory.\n";
std::cout << "\nNew Item " << partName << " added to the Inventory.\n\n";
break;
}
case 2:
{
util::clear();
std::cout << "Select Item to Restock\n";
auto inventoryItems = m_controller.getInventoryItems();
addQuantityToItem(inventoryItems, m_controller);
break;
}
default:
{
std::cout << "\nEnter a valid choice.\n\n";
}
}
util::pressEnter();
}
@@ -273,8 +306,10 @@ void AdminMenu::checkStockAvailability()
{
util::clear();
std::string itemId;
std::cout << "Enter the Item Id : ";
std::cout << "Check Stock Availability \n";
std::cout << "Enter the Item ID : ";
util::read(itemId);
util::clear();
const InventoryItem* selectedItem = m_controller.getInventoryItem(itemId);
if (selectedItem != nullptr)
{
@@ -287,6 +322,10 @@ void AdminMenu::checkStockAvailability()
std::cout << "Quantity : " << selectedItem->getQuantity() << "\n";
}
}
else
{
std::cout << "Item not Found" << std::endl;
}
util::pressEnter();
}
@@ -302,14 +341,16 @@ Returns:
void AdminMenu::assignJob()
{
util::clear();
std::cout << "Assign Job to Technician\n";
std::string selectedService;
bool hasPendingService = false;
auto currentBookings = m_controller.getServiceBookings();
auto pendingServiceBookings = filterActiveServiceBookings(currentBookings);
auto availableTechnicians = m_controller.getUsers(util::UserType::TECHNICIAN);
int bookingsSize = currentBookings.getSize();
int bookingsSize = pendingServiceBookings.getSize();
util::Map<int, const ServiceBooking*> serviceBookingsMap;
util::Map<int, const User*> currentAvailableTechniciansMap;
if (listServiceBookings(currentBookings, bookingsSize, serviceBookingsMap))
if (listServiceBookings(pendingServiceBookings, bookingsSize, serviceBookingsMap))
{
const ServiceBooking* selectedService = selectPendingServiceBookings(serviceBookingsMap);
if (selectedService)
@@ -325,14 +366,19 @@ void AdminMenu::assignJob()
{
m_controller.createJobCard(selectedService->getId(), selectedTechnician->getId(), servicesInBooking.getValueAt(iterator)->getId());
}
std::cout << "Job card created for each service and technician successfully assigned.\n\n";
}
}
else
{
std::cout << "No technicians are currently available.";
std::cout << "No technicians are currently available.\n\n";
}
}
}
else
{
std::cout << "No pending service bookings available.\n\n";
}
util::pressEnter();
}
@@ -347,17 +393,19 @@ Returns:
void AdminMenu::createService()
{
util::clear();
std::cout << "Create Service\n";
std::string serviceName;
double labourCost;
std::cout << "Enter the service name: ";
util::read(serviceName);
util::Map<std::string, const InventoryItem*> currentInventoryItems = m_controller.getInventoryItems();
util::Map<std::string, const InventoryItem*> activeInventoryItems = filterActiveItems(currentInventoryItems);
util::Vector<std::string> selectedInventoryItems;
selectInventoryItems(currentInventoryItems,selectedInventoryItems);
std::cout << "Enter the labour cost: ";
selectInventoryItems(activeInventoryItems,selectedInventoryItems);
std::cout << "\nEnter the labour cost: ";
util::read(labourCost);
m_controller.createService(serviceName, selectedInventoryItems, labourCost);
std::cout << "Service created sucessfully.\n";
std::cout << "\nService created sucessfully.\n\n";
util::pressEnter();
}
@@ -372,17 +420,19 @@ Returns:
void AdminMenu::removeService()
{
util::clear();
std::cout << "Remove Service\n";
std::string selectedServiceID;
util::Map<std::string, const Service*> currentServices = m_controller.getServices();
selectedServiceID = selectServicesToRemove(currentServices);
util::Map<std::string, const Service*> currentActiveServices = filterActiveServices(currentServices);
selectedServiceID = selectServicesToRemove(currentActiveServices);
if (selectedServiceID != "")
{
m_controller.removeService(selectedServiceID);
std::cout << "Service removed sucessfully.";
std::cout << "Service removed sucessfully.\n\n";
}
else
{
std::cout << "Failed to remove service.";
std::cout << "Failed to remove service.\n\n";
}
util::pressEnter();
}
@@ -397,36 +447,37 @@ void AdminMenu::addTechnician()
{
util::clear();
std::string username, name, password, email, phoneNumber;
std::cout << std::left << std::setw(25) << "Enter Technician Username: ";
std::cout << "Add Technician\n";
std::cout << "Enter Technician Username: ";
util::read(username);
std::cout << std::left << std::setw(25) << "Enter Technician Name: ";
std::cout << "Enter Technician Name: ";
util::read(name);
std::cout << std::setw(25) << "Enter Technician Password: ";
std::cout << "Enter Technician Password: ";
util::read(password);
if(!util::isPasswordValid(password))
{
std::cout << "Error: Password is invalid!";
std::cout << "\nError: Password is invalid!\n\n";
util::pressEnter();
return;
}
std::cout << std::setw(25) << "Enter Technician Email: ";
std::cout << "Enter Technician Email: ";
util::read(email);
if(!util::isEmailValid(email))
{
std::cout << "Error: Email is invalid!";
std::cout << "\nError: Email is invalid!\n\n";
util::pressEnter();
return;
}
std::cout << std::setw(25) << "Enter Technician Phone: ";
std::cout << "Enter Technician Phone: ";
util::read(phoneNumber);
if(!util::isPhoneNumberValid(phoneNumber))
{
std::cout << "Error: Phone Number is invalid!";
std::cout << "\nError: Phone Number is invalid!\n\n";
util::pressEnter();
return;
}
m_controller.createTechnician(username, name, password, email, phoneNumber);
std::cout << "\nTechnician Added Successfully.\n";
std::cout << "\nTechnician Added Successfully.\n\n";
util::pressEnter();
}
@@ -443,6 +494,7 @@ void AdminMenu::removeUser()
auto listOfUsers = m_controller.getUsers();
auto listOfActiveUsers = filterActiveUsers(listOfUsers);
int activeUserCount = listOfActiveUsers.getSize();
std::cout << "Remove User \n";
if (activeUserCount < 1)
{
std::cout << "No Active users." << std::endl;
@@ -477,7 +529,10 @@ Return type: void
void AdminMenu::createComboPackages()
{
util::clear();
std::cout << "Create Combo Packages\n";
auto serviceList = m_controller.getServices();
auto activeServices = filterActiveServices(serviceList);
int currentActiveServicesCount = activeServices.getSize();
const int NUMBER_OF_SERVICE_PER_PACKAGE = 2;
util::Vector<std::string> selectedServiceID;
for (int iterator = 0; iterator < NUMBER_OF_SERVICE_PER_PACKAGE; iterator++)
@@ -485,10 +540,10 @@ void AdminMenu::createComboPackages()
const Service* chosenService = nullptr;
while (true)
{
chosenService = selectServiceFromServices(serviceList);
chosenService = selectServiceFromServices(activeServices);
if (chosenService == nullptr)
{
std::cout << "Failed to create combo package!";
std::cout << "Failed to create combo package!\n\n";
util::pressEnter();
return;
}
@@ -503,13 +558,21 @@ void AdminMenu::createComboPackages()
}
if (alreadyChosen)
{
if (currentActiveServicesCount < 2)
{
break;
}
std::cout << "Service already selected. Please choose a different one." << std::endl;
continue;
}
selectedServiceID.push_back(chosenService->getId());
util::clear();
break;
}
if (currentActiveServicesCount < 2)
{
std::cout << "All the available services selected\n\n";
break;
}
}
std::string packageName;
double discountPercentage;
@@ -537,16 +600,17 @@ Return type: void
void AdminMenu::removeComboPackage()
{
util::clear();
std::cout << "Remove Combo Package\n";
util::Map<std::string, const ComboPackage*> currentComboPackages = m_controller.getComboPackages();
std::string selectedComboPackageID = selectComboPackage(currentComboPackages);
if (selectedComboPackageID != "")
if (!selectedComboPackageID.empty())
{
m_controller.removeComboPackage(selectedComboPackageID);
std::cout << "Combo Package removed successfully.\n";
std::cout << "Combo Package removed successfully.\n\n";
}
else
{
std::cout << "Combo package removal failed.\n";
std::cout << "Combo package removal failed.\n\n";
}
util::pressEnter();
}
@@ -144,11 +144,12 @@ void CustomerMenu::updateDetails()
{
std::string email, phone;
util::clear();
std::cout << "Update Details\n";
std::cout << "Enter new email: ";
util::read(email);
if (!util::isEmailValid(email))
{
std::cout << "Error: Email is invalid!";
std::cout << "Error: Email is invalid!\n";
util::pressEnter();
return;
}
@@ -156,12 +157,12 @@ void CustomerMenu::updateDetails()
util::read(phone);
if (!util::isPhoneNumberValid(phone))
{
std::cout << "Error: Phone number is invalid!";
std::cout << "Error: Phone number is invalid!\n";
util::pressEnter();
return;
}
m_controller.updateUserDetails(email, phone);
std::cout << "Profile details updated successfully";
std::cout << "Profile details updated successfully\n";
util::pressEnter();
}
@@ -260,16 +261,17 @@ void CustomerMenu::viewServiceHistory()
const User* currentUser = m_controller.getAuthenticatedUser();
std::string currentUserID = currentUser->getId();
util::Map<std::string, const ServiceBooking*> serviceBookingsByCurrentUser = m_controller.getServiceBookingsByUser(currentUserID);
std::cout << "View Service History" << std::endl;
if (serviceBookingsByCurrentUser.getSize() != 0)
{
std::cout << std::left
<< std::setw(12) << "Booking ID"
<< std::setw(15) << "Booking ID"
<< std::setw(20) << "Technician"
<< std::setw(15) << "Vehicle Brand"
<< std::setw(15) << "Vehicle Number"
<< std::setw(15) << "Vehicle Model"
<< std::setw(10) << "Discount %"
<< std::setw(12) << "Status"
<< std::setw(20) << "Vehicle Brand"
<< std::setw(20) << "Vehicle Number"
<< std::setw(20) << "Vehicle Model"
<< std::setw(20) << "Discount %"
<< std::setw(20) << "Status"
<< std::endl;
for (int iterator = 0; iterator < serviceBookingsByCurrentUser.getSize(); iterator++)
{
@@ -278,13 +280,13 @@ void CustomerMenu::viewServiceHistory()
? "Not Assigned"
: currentBooking->getAssignedTechnician()->getName();
std::cout << std::left
<< std::setw(12) << currentBooking->getId()
<< std::setw(15) << currentBooking->getId()
<< std::setw(20) << technicianName
<< std::setw(15) << currentBooking->getVehicleBrand()
<< std::setw(15) << currentBooking->getVehicleNumber()
<< std::setw(15) << currentBooking->getVehicleModel()
<< std::setw(10) << currentBooking->getDiscountPercentage()
<< std::setw(12) << util::getServiceJobStatusString(currentBooking->getStatus())
<< std::setw(20) << currentBooking->getVehicleBrand()
<< std::setw(20) << currentBooking->getVehicleNumber()
<< std::setw(20) << currentBooking->getVehicleModel()
<< std::setw(20) << currentBooking->getDiscountPercentage()
<< std::setw(20) << util::getServiceJobStatusString(currentBooking->getStatus())
<< std::endl;
hasServiceHistory = true;
}
@@ -308,11 +310,35 @@ Returns:
void CustomerMenu::completePayments()
{
util::clear();
std::cout << "Complete Payments\n";
util::Map<std::string, const Invoice*> currentInvoices = m_controller.getInvoicesByUser();
if (currentInvoices.isEmpty())
{
std::cout << "No pending invoices available for payment.\n";
util::pressEnter();
return;
}
bool hasPending = false;
for (int index = 0; index < currentInvoices.getSize(); ++index)
{
const Invoice* invoice = currentInvoices.getValueAt(index);
if (invoice && invoice->getStatus() == util::PaymentStatus::PENDING)
{
hasPending = true;
break;
}
}
if (!hasPending)
{
std::cout << "No pending invoices available for payment.\n";
util::pressEnter();
return;
}
std::string selectedID = selectInvoiceFromUserForPayment(currentInvoices);
if (selectedID == "")
{
std::cout << "Payment failed.\n";
util::pressEnter();
return;
}
util::PaymentMode paymentMode = selectPaymentMode();
@@ -332,6 +358,7 @@ Returns:
void CustomerMenu::viewInvoices()
{
util::clear();
std::cout << "View Invoices\n";
util::Map<std::string, const Invoice*> currentUserInvoices = m_controller.getInvoicesByUser();
displayInvoices(currentUserInvoices);
util::pressEnter();
@@ -367,4 +394,4 @@ void CustomerMenu::configureNotifications()
util::clear();
std::cout << "Notification preferences updated successfully.\n";
util::pressEnter();
}
}
@@ -39,8 +39,12 @@ Returns:
*/
inline std::string selectServicesToRemove(util::Map<std::string, const Service*> currentServices)
{
if (currentServices.getSize() == 0)
{
std::cout << "No Services Currently Available.\n";
return "";
}
util::Map<int, const Service*> currentServicesMap;
bool hasServices = false;
int currentIndex = 1, choice;
std::cout << std::left
<< std::setw(6) << "Index"
@@ -51,23 +55,17 @@ inline std::string selectServicesToRemove(util::Map<std::string, const Service*>
for (int iterator = 0; iterator < currentServices.getSize(); iterator++)
{
const Service* currentService = currentServices.getValueAt(iterator);
if (currentService->getState() == util::State::INACTIVE)
if (currentService == nullptr || currentService->getState() == util::State::INACTIVE)
{
continue;
}
std::cout << std::left
<< std::setw(6) << currentIndex
std::cout << std::left
<< std::setw(6) << currentIndex
<< std::setw(12) << currentService->getId()
<< std::setw(20) << currentService->getName()
<< std::setw(10) << currentService->getLaborCost()
<< std::endl;
hasServices = true;
<< std::endl;
currentServicesMap.insert(currentIndex++, currentService);
}
if (!hasServices)
{
std::cout << "No services currently available." << std::endl;
return "";
}
std::cout << "Enter your choice: ";
util::read(choice);
@@ -93,69 +91,89 @@ Returns:
*/
inline void selectInventoryItems(util::Map<std::string, const InventoryItem*>& currentInventoryItems, util::Vector<std::string>& selectedInventoryItems)
{
bool doRun = true, hasInventoryItems = false;
util::Map<int, const InventoryItem*> currentInventoryMap;
int currentIndex = 1;
int choice;
if (currentInventoryItems.getSize() == 0)
{
std::cout << "Inventory empty.";
}
while (doRun)
{
bool hasInventoryItems = false;
int currentIndex = 1;
currentInventoryMap.clear();
std::cout << std::left
<< std::setw(6) << "Index"
<< std::setw(12) << "Item ID"
<< std::setw(20) << "Part Name"
<< std::setw(10) << "Price"
<< std::setw(10) << "Quantity"
<< std::endl;
for (int iterator = 0; iterator < currentInventoryItems.getSize(); iterator++)
bool doRun = true;
util::Map<int, const InventoryItem*> currentInventoryMap;
int choice;
if (currentInventoryItems.getSize() == 0)
{
const InventoryItem* currentInventoryItem = currentInventoryItems.getValueAt(iterator);
if (currentInventoryItem->getState() == util::State::INACTIVE)
{
continue;
}
std::cout << std::left
<< std::setw(6) << currentIndex
<< std::setw(12) << currentInventoryItem->getId()
<< std::setw(20) << currentInventoryItem->getPartName()
<< std::setw(10) << currentInventoryItem->getPrice()
<< std::setw(10) << currentInventoryItem->getQuantity()
<< std::endl;
hasInventoryItems = true;
currentInventoryMap.insert(currentIndex++, currentInventoryItem);
}
if (!hasInventoryItems)
{
std::cout << "No items present in the inventory." << std::endl;
doRun = false;
break;
std::cout << "No Items Present, Inventory empty.\n";
return;
}
std::cout << "Select the item (Index) or enter -1 to exit: ";
util::read(choice);
if (choice == -1)
{
doRun = false;
while (doRun)
{
std::cout << "\nSelect Required Items\n";
bool hasInventoryItems = false;
int currentIndex = 1;
currentInventoryMap.clear();
std::cout << std::left
<< std::setw(6) << "Index"
<< std::setw(12) << "Item ID"
<< std::setw(20) << "Part Name"
<< std::setw(10) << "Price"
<< std::setw(10) << "Quantity"
<< std::endl;
for (int iterator = 0; iterator < currentInventoryItems.getSize(); iterator++)
{
const InventoryItem* currentInventoryItem = currentInventoryItems.getValueAt(iterator);
if (currentInventoryItem == nullptr || currentInventoryItem->getState() == util::State::INACTIVE)
{
continue;
}
std::cout << std::left
<< std::setw(6) << currentIndex
<< std::setw(12) << currentInventoryItem->getId()
<< std::setw(20) << currentInventoryItem->getPartName()
<< std::setw(10) << currentInventoryItem->getPrice()
<< std::setw(10) << currentInventoryItem->getQuantity()
<< std::endl;
currentInventoryMap.insert(currentIndex++, currentInventoryItem);
hasInventoryItems = true;
}
else if (currentInventoryMap.find(choice) != -1)
{
selectedInventoryItems.push_back(currentInventoryMap.getValueAt(choice)->getId());
std::cout << "Item added successfully." << std::endl;
if (!hasInventoryItems)
{
break;
}
std::cout << "Select the item (Index) or enter -1 to exit: ";
util::read(choice);
if (choice == -1)
{
doRun = false;
}
else if (currentInventoryMap.find(choice) != -1)
{
selectedInventoryItems.push_back(currentInventoryMap.getValueAt(currentInventoryMap.find(choice))->getId());
std::cout << "Item added successfully.\n" << std::endl;
}
else
{
std::cout << "Enter a valid integer." << std::endl;
std::cout << "Enter a valid integer.\n" << std::endl;
}
}
}
/*
Function: filterActiveServiceBookings
Description: Filters the given service bookings and returns only bookings with PENDING status.
Parameters:
- currentBookings: util::Map<std::string, const ServiceBooking*>, collection of current service bookings
Returns:
- util::Map<std::string, const ServiceBooking*>: map containing only active (PENDING) service bookings
*/
inline util::Map<std::string, const ServiceBooking*> filterActiveServiceBookings(util::Map<std::string, const ServiceBooking*> currentBookings)
{
util::Map<std::string, const ServiceBooking*> activeServiceBookings;
for (int iterator = 0; iterator < currentBookings.getSize(); iterator++)
{
const ServiceBooking* currentServiceBooking = currentBookings.getValueAt(iterator);
if (currentServiceBooking && currentServiceBooking->getStatus() == util::ServiceJobStatus::PENDING)
{
activeServiceBookings.insert(currentServiceBooking->getId(), currentServiceBooking);
}
}
return activeServiceBookings;
}
/*
Function: listServiceBookings
Description: Lists all pending service bookings and maps them to indices for selection.
@@ -168,48 +186,46 @@ Returns:
*/
inline bool listServiceBookings(util::Map<std::string, const ServiceBooking*>& currentBookings, int& bookingsSize, util::Map<int, const ServiceBooking*>& serviceBookingsMap)
{
if (currentBookings.getSize() == 0)
{
return false;
}
int currentIndex = 1;
bool hasPendingService = false;
std::cout << "\nSelect Service Booking" << std::endl;
std::cout << std::left
<< std::setw(10) << "Index"
<< std::setw(10) << "ID"
<< std::setw(12) << "Status"
<< std::setw(12) << "CustID"
<< std::setw(20) << "Customer"
<< std::setw(15) << "VehicleNo"
<< std::setw(15) << "Brand"
<< std::setw(15) << "Model"
<< std::setw(20) << "Technician"
<< std::setw(15) << "TechnicianID"
<< std::setw(10) << "ID"
<< std::setw(12) << "Status"
<< std::setw(12) << "CustID"
<< std::setw(20) << "Customer"
<< std::setw(15) << "VehicleNo"
<< std::setw(15) << "Brand"
<< std::setw(15) << "Model"
<< std::setw(20) << "Technician"
<< std::setw(15) << "TechnicianID"
<< std::endl;
for (int iterator = 0; iterator < bookingsSize; iterator++)
for (int iterator = 0; iterator < bookingsSize; iterator++)
{
const ServiceBooking* currentBooking = currentBookings.getValueAt(iterator);
if (currentBooking && currentBooking->getStatus() == util::ServiceJobStatus::PENDING)
const ServiceBooking* currentBooking = currentBookings.getValueAt(iterator);
if (currentBooking && currentBooking->getStatus() == util::ServiceJobStatus::PENDING)
{
hasPendingService = true;
const User* currentAssignedTechnician = currentBooking->getAssignedTechnician();
std::cout << std::left
<< std::setw(10) << currentIndex
<< std::setw(10) << currentBooking->getId()
<< std::setw(12) << util::getServiceJobStatusString(currentBooking->getStatus())
<< std::setw(12) << currentBooking->getCustomerId()
<< std::setw(20) << currentBooking->getCustomer()->getName()
<< std::setw(15) << currentBooking->getVehicleNumber()
<< std::setw(15) << currentBooking->getVehicleBrand()
<< std::setw(15) << currentBooking->getVehicleModel()
<< std::setw(20) << ((currentAssignedTechnician == nullptr || currentAssignedTechnician->getName().empty()) ? "Null" : currentAssignedTechnician->getName())
<< std::setw(15) << ((currentAssignedTechnician == nullptr || currentAssignedTechnician->getId().empty()) ? "Null" : currentAssignedTechnician->getId())
<< std::endl;
serviceBookingsMap.insert(currentIndex++, currentBooking);
const User* currentAssignedTechnician = currentBooking->getAssignedTechnician();
std::cout << std::left
<< std::setw(10) << currentIndex
<< std::setw(10) << currentBooking->getId()
<< std::setw(12) << util::getServiceJobStatusString(currentBooking->getStatus())
<< std::setw(12) << currentBooking->getCustomerId()
<< std::setw(20) << currentBooking->getCustomer()->getName()
<< std::setw(15) << currentBooking->getVehicleNumber()
<< std::setw(15) << currentBooking->getVehicleBrand()
<< std::setw(15) << currentBooking->getVehicleModel()
<< std::setw(20) << ((currentAssignedTechnician == nullptr || currentAssignedTechnician->getName().empty()) ? "NULL" : currentAssignedTechnician->getName())
<< std::setw(15) << ((currentAssignedTechnician == nullptr || currentAssignedTechnician->getId().empty()) ? "NULL" : currentAssignedTechnician->getId())
<< std::endl;
serviceBookingsMap.insert(currentIndex++, currentBooking);
}
}
}
if (!hasPendingService)
{
std::cout << "No pending service available." << std::endl;
return false;
}
return true;
return true;
}
/*
@@ -223,15 +239,15 @@ Returns:
inline const ServiceBooking* selectPendingServiceBookings(util::Map<int, const ServiceBooking*>& serviceBookingsMap)
{
int userInputIndex;
std::cout << "Enter a valid service index: ";
std::cout << "\nEnter a valid service index: ";
util::read(userInputIndex);
if (serviceBookingsMap.find(userInputIndex) != -1)
{
return serviceBookingsMap.getValueAt(userInputIndex);
return serviceBookingsMap.getValueAt(serviceBookingsMap.find(userInputIndex));
}
else
{
std::cout << "Enter a valid index.";
std::cout << "Enter a valid index.\n\n";
return nullptr;
}
}
@@ -250,6 +266,7 @@ inline void listAvailableTechnicians(util::Map<std::string, const User*> current
{
bool hasTechnicians = false;
int currentIndex = 1;
std::cout << "\nSelect Technician\n";
std::cout << std::left
<< std::setw(6) << "Index"
<< std::setw(15) << "Technician ID"
@@ -272,7 +289,7 @@ inline void listAvailableTechnicians(util::Map<std::string, const User*> current
}
if (!hasTechnicians)
{
std::cout << "No technicians currently available.";
std::cout << "No technicians currently available.\n\n";
}
}
@@ -285,17 +302,18 @@ Returns:
- const User*: Pointer to the selected technician, or nullptr if invalid
*/
inline const User* selectTechnician(util::Map<int, const User*>& currentAvailableTechniciansMap)
{
int userInputIndex;
std::cout << "\nEnter valid technician index: ";
util::read(userInputIndex);
if (currentAvailableTechniciansMap.find(userInputIndex) != -1)
{
int userInputIndex;
util::read(userInputIndex);
if (currentAvailableTechniciansMap.find(userInputIndex) != -1)
{
return currentAvailableTechniciansMap.getValueAt(userInputIndex);
}
else
{
std::cout << "Enter a valid index.";
return nullptr;
return currentAvailableTechniciansMap.getValueAt(currentAvailableTechniciansMap.find(userInputIndex));
}
else
{
std::cout << "Enter a valid index.\n\n";
return nullptr;
}
}
@@ -311,36 +329,36 @@ inline std::string selectInvoiceFromUserForPayment(const util::Map<std::string,
{
int currentIndex = 1, choice;
util::Map<int, const Invoice*> pendingInvoicesForPayment;
std::cout << std::left
<< std::setw(6) << "Index"
<< std::setw(12) << "BookingID"
<< std::setw(15) << "VehicleBrand"
<< std::setw(15) << "VehicleNumber"
<< std::setw(12) << "TechID"
<< std::setw(20) << "TechnicianName"
<< std::setw(10) << "Discount(%)"
<< std::setw(12) << "TotalAmount"
<< std::setw(20) << "InvoiceDate"
<< std::endl;
std::cout << std::left
<< std::setw(8) << "Index"
<< std::setw(15) << "Booking ID"
<< std::setw(20) << "Vehicle Brand"
<< std::setw(20) << "Vehicle Number"
<< std::setw(18) << "Technician ID"
<< std::setw(25) << "Technician Name"
<< std::setw(15) << "Discount(%)"
<< std::setw(15) << "TotalAmount"
<< std::setw(22) << "Invoice Timestamp"
<< std::endl;
for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++)
{
const Invoice* currentInvoice = currentInvoices.getValueAt(iterator);
if (currentInvoice && currentInvoice->getStatus() == util::PaymentStatus::PENDING)
{
const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician();
std::cout << std::left
<< std::setw(6) << currentIndex
<< std::setw(12) << currentInvoice->getBookingId()
<< std::setw(15) << currentInvoice->getBooking()->getVehicleBrand()
<< std::setw(15) << currentInvoice->getBooking()->getVehicleNumber()
<< std::setw(12) << ((currentTechnician != nullptr && currentTechnician->getId() != "") ?
currentTechnician->getId() : "Null")
<< std::setw(20) << ((currentTechnician != nullptr && currentTechnician->getName() != "") ?
currentTechnician->getName() : "Null")
<< std::setw(10) << currentInvoice->getDiscountPercentage()
<< std::setw(12) << currentInvoice->getTotalAmount()
<< std::setw(20) << currentInvoice->getInvoiceDate().toString()
<< std::endl;
std::cout << std::left
<< std::setw(8) << currentIndex
<< std::setw(15) << currentInvoice->getBookingId()
<< std::setw(20) << currentInvoice->getBooking()->getVehicleBrand()
<< std::setw(20) << currentInvoice->getBooking()->getVehicleNumber()
<< std::setw(18) << ((currentTechnician != nullptr && currentTechnician->getId() != "") ?
currentTechnician->getId() : "Null")
<< std::setw(25) << ((currentTechnician != nullptr && currentTechnician->getName() != "") ?
currentTechnician->getName() : "Null")
<< std::setw(15) << currentInvoice->getDiscountPercentage()
<< std::setw(15) << currentInvoice->getTotalAmount()
<< std::setw(22) << currentInvoice->getInvoiceDate().toString()
<< std::endl;
pendingInvoicesForPayment.insert(currentIndex++, currentInvoice);
}
}
@@ -374,23 +392,27 @@ Returns:
*/
inline util::PaymentMode selectPaymentMode()
{
int choice;
std::cout << "Enter the payment Mode\n1.OFFLINE\n2.ONLINE\nChoice: ";
util::read(choice);
if (choice == 1)
int choice;
while (true)
{
std::cout << "Offline mode selected.\n";
return util::PaymentMode::OFFLINE;
}
else if (choice == 2)
util::clear();
std::cout << "Enter the payment Mode\n1.OFFLINE\n2.ONLINE\nChoice: ";
util::read(choice);
if (choice == 1)
{
std::cout << "Online mode selected.\n";
return util::PaymentMode::ONLINE;
std::cout << "Offline mode selected.\n";
return util::PaymentMode::OFFLINE;
}
else if (choice == 2)
{
std::cout << "Online mode selected.\n";
return util::PaymentMode::ONLINE;
}
else
{
std::cout << "Invalid choice. Try again.\n";
util::pressEnter();
}
else
{
std::cout << "Invalid choice. Offline mode selected.\n";
return util::PaymentMode::OFFLINE;
}
}
@@ -407,59 +429,63 @@ Throws:
*/
inline void displayInvoices(util::Map<std::string, const Invoice*> currentUserInvoices)
{
if (currentUserInvoices.getSize() == 0)
if (currentUserInvoices.getSize() == 0)
{
std::cout << "No invoices found for this account." << std::endl;
util::pressEnter();
std::cout << "No invoices found for this account." << std::endl << std::endl;
return;
}
else
else
{
for (int index = 0; index < currentUserInvoices.getSize(); index++)
{
const Invoice* currentInvoice = currentUserInvoices.getValueAt(index);
if (currentInvoice)
{
const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician();
std::cout << "\nInvoice Details\n";
std::cout << "Booking ID: " << currentInvoice->getBookingId() << std::endl;
std::cout << "Vehicle Brand: " << currentInvoice->getBooking()->getVehicleBrand() << std::endl;
std::cout << "Vehicle Number: " << currentInvoice->getBooking()->getVehicleNumber() << std::endl;
std::cout << "Technician ID: " <<
((currentTechnician != nullptr && currentTechnician->getId() != "") ?
currentTechnician->getId() : "Null") << std::endl;
std::cout << "Technician Name: " <<
((currentTechnician != nullptr && currentTechnician->getName() != "") ?
currentTechnician->getName() : "Null") << std::endl;
std::cout << "Discount(%): " << currentInvoice->getDiscountPercentage() << std::endl;
std::cout << "Total Amount: " << currentInvoice->getTotalAmount() << std::endl;
std::cout << "Invoice Date: " << currentInvoice->getInvoiceDate().toString() << std::endl;
std::cout << "Payment Status: " << util::getPaymentStatusString(currentInvoice->getStatus()) << std::endl;
auto inventoryItemsInInvoice = currentInvoice->getParts();
std::cout << "\nItems Used:\n";
std::cout << std::left
<< std::setw(20) << "ItemName"
<< std::setw(10) << "Quantity"
<< std::setw(10) << "Price"
<< std::endl;
std::cout << std::string(40, '-') << std::endl;
for (int iterator = 0; iterator < inventoryItemsInInvoice.getSize(); iterator++)
{
InventoryItem* currentItem = inventoryItemsInInvoice.getValueAt(iterator);
std::cout << std::left
<< std::setw(20) << currentItem->getPartName()
<< std::setw(10) << currentItem->getQuantity()
<< std::setw(10) << currentItem->getPrice()
<< std::endl;
}
}
else
for (int index = 0; index < currentUserInvoices.getSize(); index++)
{
throw std::runtime_error("Null invoice encountered while displaying invoices.");
util::pressEnter();
}
const Invoice* currentInvoice = currentUserInvoices.getValueAt(index);
if (currentInvoice)
{
const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician();
std::cout << "\nInvoice Details\n";
std::cout << "Booking ID: " << currentInvoice->getBookingId() << std::endl;
std::cout << "Vehicle Brand: " << currentInvoice->getBooking()->getVehicleBrand() << std::endl;
std::cout << "Vehicle Number: " << currentInvoice->getBooking()->getVehicleNumber() << std::endl;
std::cout << "Technician ID: " <<
((currentTechnician != nullptr && currentTechnician->getId() != "") ?
currentTechnician->getId() : "Null") << std::endl;
std::cout << "Technician Name: " <<
((currentTechnician != nullptr && currentTechnician->getName() != "") ?
currentTechnician->getName() : "Null") << std::endl;
std::cout << "Discount(%): " << currentInvoice->getDiscountPercentage() << std::endl;
std::cout << "Total Amount: " << currentInvoice->getTotalAmount() << std::endl;
std::cout << "Invoice Date: " << currentInvoice->getInvoiceDate().toString() << std::endl;
std::cout << "Payment Status: " << util::getPaymentStatusString(currentInvoice->getStatus()) << std::endl;
auto inventoryItemsInInvoice = currentInvoice->getParts();
if (inventoryItemsInInvoice.isEmpty())
{
std::cout << "No inventory items used.\n\n";
continue;
}
std::cout << "\nItems Used:\n";
std::cout << std::left
<< std::setw(20) << "ItemName"
<< std::setw(10) << "Quantity"
<< std::setw(10) << "Price"
<< std::endl;
std::cout << std::string(40, '-') << std::endl;
for (int iterator = 0; iterator < inventoryItemsInInvoice.getSize(); iterator++)
{
InventoryItem* currentItem = inventoryItemsInInvoice.getValueAt(iterator);
std::cout << std::left
<< std::setw(20) << currentItem->getPartName()
<< std::setw(10) << currentItem->getQuantity()
<< std::setw(10) << currentItem->getPrice()
<< std::endl;
}
}
else
{
throw std::runtime_error("Null invoice encountered while displaying invoices.");
}
}
}
util::pressEnter();
}
/*
@@ -528,16 +554,11 @@ Return type: const Notification* - pointer to the selected notification
*/
inline const Notification* selectNotification(const util::Vector<const Notification*>& notifications)
{
if (notifications.getSize() == 0)
{
std::cout << "No notifications available." << std::endl;
return nullptr;
}
util::Map<int, const Notification*> indexedNotifications;
std::cout << std::left
<< std::setw(6) << "Index"
<< std::setw(10) << "Index"
<< std::setw(15) << "ID"
<< std::setw(30) << "Title"
<< std::setw(35) << "Title"
<< std::setw(25) << "Timestamp"
<< std::endl;
int currentIndex = 1;
@@ -547,9 +568,9 @@ inline const Notification* selectNotification(const util::Vector<const Notificat
if (currentNotification)
{
std::cout << std::left
<< std::setw(6) << currentIndex
<< std::setw(10) << currentIndex
<< std::setw(15) << currentNotification->getId()
<< std::setw(30) << currentNotification->getTitle()
<< std::setw(35) << util::truncateString(currentNotification->getTitle(), 30)
<< std::setw(25) << currentNotification->getCreatedAt().toString()
<< std::endl;
indexedNotifications.insert(currentIndex, currentNotification);
@@ -602,6 +623,13 @@ inline void viewAndDeleteNotification(Controller& controller)
{
util::clear();
auto notifications = controller.getNotifications();
std::cout << "View and Delete Notification" << std::endl;
if (notifications.getSize() == 0)
{
std::cout << "No notifications available." << std::endl;
util::pressEnter();
return;
}
const Notification* selectedNotification = selectNotification(notifications);
if (!selectedNotification)
{
@@ -675,7 +703,7 @@ inline util::Map<std::string, const User*> filterActiveUsers(const util::Map<std
for (int index = 0; index < inventorySize; index++)
{
const User* user = listOfUsers.getValueAt(index);
if (user != nullptr && user->getState() != util::State::INACTIVE)
if (user != nullptr && user->getState() != util::State::INACTIVE && user->getUserType() != util::UserType::ADMIN)
{
activeUsers.insert(user->getId(), user);
}
@@ -717,6 +745,28 @@ inline void displayAllActiveUsers(util::Map<std::string, const User*>& activeUse
}
}
/*
Function: filterActiveServices
Description: Filters the given list of services and returns only those that are active.
Parameters:
- serviceList: Map of service IDs to Service pointers.
Returns:
- util::Map<std::string, const Service*> containing only active services.
*/
inline util::Map<std::string, const Service*> filterActiveServices(util::Map<std::string, const Service*> serviceList)
{
util::Map<std::string, const Service*> activeServices;
for (int iterator = 0; iterator < serviceList.getSize(); iterator++)
{
const Service* currentService = serviceList.getValueAt(iterator);
if (currentService && currentService->getState() == util::State::ACTIVE)
{
activeServices.insert(currentService->getId(), currentService);
}
}
return activeServices;
}
/*
Function: selectServiceFromServices
Description: Displays active services and allows the customer to select one by index.
@@ -725,6 +775,11 @@ Return type: const Service* - selected service
*/
inline const Service* selectServiceFromServices(const util::Map<std::string, const Service*>& services)
{
if (services.getSize() == 0)
{
std::cout << "No active services available." << std::endl;
return nullptr;
}
util::Map<int, const Service*> activeServicesMap;
int currentIndex = 1;
int userInputIndex;
@@ -833,7 +888,7 @@ inline bool getNotificationPreference(const std::string& serviceName)
while (true)
{
util::clear();
std::cout << " Configure Notification Preferences\n";
std::cout << "Configure Notification Preferences\n";
std::cout << "\n" << serviceName << " Notifications\n";
std::cout << "1. Enable Notifications\n";
std::cout << "2. Disable Notifications\n";
@@ -905,6 +960,7 @@ inline void displayInventoryWithItems(util::Map<std::string, const InventoryItem
<< std::endl;
}
}
std::cout << std::endl;
}
/*
@@ -924,7 +980,7 @@ inline void addQuantityToItem(util::Map<std::string, const InventoryItem*>& inve
int activeSize = activeItems.getSize();
if (activeSize == 0)
{
std::cout << "No active items available in Inventory" << std::endl;
std::cout << "\nNo active items available in Inventory" << std::endl << std::endl;
return;
}
displayInventoryWithItems(activeItems);
@@ -932,7 +988,7 @@ inline void addQuantityToItem(util::Map<std::string, const InventoryItem*>& inve
util::read(itemIndex);
if (itemIndex < 1 || itemIndex > activeSize)
{
std::cout << "Invalid index selected." << std::endl;
std::cout << "\nInvalid index selected." << std::endl << std::endl;
return;
}
std::cout << "Enter quantity to add: ";
@@ -947,13 +1003,14 @@ inline void addQuantityToItem(util::Map<std::string, const InventoryItem*>& inve
{
std::string selectedItemId = selectedItem->getId();
m_controller.addInventoryItemStock(selectedItemId, quantity);
std::cout << "Updated " << selectedItem->getPartName()
std::cout << "\nUpdated " << selectedItem->getPartName()
<< " stock. New quantity: " << selectedItem->getQuantity()
<< std::endl
<< std::endl;
}
else
{
std::cout << "Error: Selected item could not be found." << std::endl;
std::cout << "\nError: Selected item could not be found." << std::endl << std::endl;
}
}
@@ -1001,7 +1058,8 @@ inline std::string selectComboPackage(util::Map<std::string, const ComboPackage*
util::Map<int, const ComboPackage*> currentComboPackageIndexMap;
if (currentComboPackages.getSize() == 0)
{
throw std::runtime_error("No combo packages are available.\n");
std::cout << "No combo packages are available.\n";
return "";
}
int currentIndex = 1, choice, selectedIndex;
for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++)
@@ -1014,7 +1072,8 @@ inline std::string selectComboPackage(util::Map<std::string, const ComboPackage*
}
if (currentComboPackageIndexMap.getSize() == 0)
{
throw std::runtime_error("No combo packages currently active.");
std::cout << "No combo packages currently active.\n";
return "";
}
displayComboPackagesWithIndex(currentComboPackageIndexMap);
std::cout << "Enter your choice(Index): ";
@@ -6,6 +6,7 @@ Description: Implementation file containing the method definitions of the
Author: Trenser
Date:19-May-2026
*/
#include "Enums.h"
#include "InputHelper.h"
#include "JobCard.h"
@@ -7,6 +7,8 @@ Author: Trenser
Date:19-May-2026
*/
#include <iostream>
#include <stdexcept>
#include "Enums.h"
#include "InputHelper.h"
#include "OutputHelper.h"
@@ -23,29 +25,44 @@ Return type: void
*/
void UserInterface::run()
{
m_controller.loadSystemData();
m_controller.runSystemChecks();
bool isMenuActive = true;
while (isMenuActive)
try
{
try
m_controller.loadSystemData();
m_controller.runSystemChecks();
bool isMenuActive = true;
while (isMenuActive)
{
int choice;
util::clear();
std::cout << "Vehicle Service System\n1. Login\n2. Register Customer\n3. Exit\nEnter your Choice: ";
util::read(choice);
if (!handleOperation(choice))
try
{
isMenuActive = false;
int choice;
util::clear();
std::cout << "Vehicle Service System\n1. Login\n2. Register Customer\n3. Exit\nEnter your Choice: ";
util::read(choice);
if (!handleOperation(choice))
{
isMenuActive = false;
}
}
catch (const std::exception& e)
{
std::cout << "Exception: " << e.what() << std::endl;
util::pressEnter();
}
}
catch (const std::exception& e)
{
std::cout << "Exception: " << e.what() << std::endl;
util::pressEnter();
}
m_controller.saveSystemData();
}
catch (const std::invalid_argument& exception)
{
std::cout << "Exception: Invalid Argument: " << exception.what() << std::endl;
}
catch (const std::exception& exception)
{
std::cout << "Exception: " << exception.what() << std::endl;
}
catch (...)
{
std::cout << "Unknown error occurred." << std::endl;
}
m_controller.saveSystemData();
}
/*