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
This commit is contained in:
+1
-1
@@ -80,7 +80,7 @@ void InventoryManagementService::sendLowStockAlerts()
|
||||
{
|
||||
throw std::runtime_error("The system has no admins present!");
|
||||
}
|
||||
for (int index = 0; index <= inventoryItemsSize; index++)
|
||||
for (int index = 0; index < inventoryItemsSize; index++)
|
||||
{
|
||||
InventoryItem* inventoryItem = inventoryItems.getValueAt(index);
|
||||
if (inventoryItem && inventoryItem->getQuantity() < config::threshold::INVENTORY_LOW_STOCK_THRESHOLD)
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ void ServiceManagementService::purchaseService(const util::Vector<std::string>&
|
||||
Service* service = servicesMap.getValueAt(serviceIndex);
|
||||
selectedServices[service->getId()] = service;
|
||||
}
|
||||
ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::STARTED, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, 0);
|
||||
ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, 0);
|
||||
if (serviceBooking == nullptr)
|
||||
{
|
||||
throw std::runtime_error("Failed to create service booking");
|
||||
|
||||
+10
-8
@@ -19,7 +19,7 @@ Date:19-May-2026
|
||||
#include "User.h"
|
||||
#include "UserManagementService.h"
|
||||
#include "Vector.h"
|
||||
|
||||
#include "Validator.h"
|
||||
|
||||
/*
|
||||
Function: ensureAdminExists
|
||||
@@ -74,16 +74,18 @@ void UserManagementService::createUser(const std::string& username, const std::s
|
||||
PaymentManagementService paymentManagementService;
|
||||
ServiceManagementService serviceManagementService;
|
||||
auto& usersMap = m_dataStore.getUsers();
|
||||
int index = usersMap.findIf(
|
||||
[&](const std::string&, User* user)
|
||||
{
|
||||
return user->getUserName() == username;
|
||||
}
|
||||
);
|
||||
if (index != -1)
|
||||
if (util::isUsernameDuplicate(username, usersMap))
|
||||
{
|
||||
throw std::runtime_error("Username already exists");
|
||||
}
|
||||
if (util::isEmailDuplicate(email, usersMap))
|
||||
{
|
||||
throw std::runtime_error("Email already exists");
|
||||
}
|
||||
if (util::isPhoneDuplicate(phone, usersMap))
|
||||
{
|
||||
throw std::runtime_error("Phone already exists");
|
||||
}
|
||||
User* newUser = Factory::getObject<User>(username, password, name, phone, email, type);
|
||||
usersMap.insert(newUser->getId(), newUser);
|
||||
paymentManagementService.attach(newUser);
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace util
|
||||
*/
|
||||
inline void pressEnter()
|
||||
{
|
||||
std::cout << std::endl;
|
||||
system("pause");
|
||||
}
|
||||
}
|
||||
@@ -106,4 +106,37 @@ bool util::isPasswordValid(const std::string& password)
|
||||
}
|
||||
|
||||
return hasUpper && hasLower && hasDigit && hasSpecial;
|
||||
}
|
||||
}
|
||||
|
||||
bool util::isUsernameDuplicate(const std::string& username, const util::Map<std::string, User*>& usersMap)
|
||||
{
|
||||
int index = usersMap.findIf(
|
||||
[&](const std::string&, User* user)
|
||||
{
|
||||
return (user->getUserName() == username && user->getState() == util::State::ACTIVE);
|
||||
}
|
||||
);
|
||||
return index != -1;
|
||||
}
|
||||
|
||||
bool util::isPhoneDuplicate(const std::string& phone, const util::Map<std::string, User*>& usersMap)
|
||||
{
|
||||
int index = usersMap.findIf(
|
||||
[&](const std::string&, User* user)
|
||||
{
|
||||
return (user->getPhone() == phone && user->getState() == util::State::ACTIVE);
|
||||
}
|
||||
);
|
||||
return index != -1;
|
||||
}
|
||||
|
||||
bool util::isEmailDuplicate(const std::string& email, const util::Map<std::string, User*>& usersMap)
|
||||
{
|
||||
int index = usersMap.findIf(
|
||||
[&](const std::string&, User* user)
|
||||
{
|
||||
return (user->getEmail() == email && user->getState() == util::State::ACTIVE);
|
||||
}
|
||||
);
|
||||
return index != -1;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,15 @@
|
||||
#include<string>
|
||||
#include<algorithm>
|
||||
#include<cctype>
|
||||
#include "Map.h"
|
||||
#include "User.h"
|
||||
|
||||
namespace util
|
||||
{
|
||||
bool isPhoneNumberValid(const std::string&);
|
||||
bool isEmailValid(const std::string&);
|
||||
bool isPasswordValid(const std::string&);
|
||||
bool isUsernameDuplicate(const std::string&, const util::Map<std::string, User*>&);
|
||||
bool isPhoneDuplicate(const std::string&, const util::Map<std::string, User*>&);
|
||||
bool isEmailDuplicate(const std::string&, const util::Map<std::string, User*>&);
|
||||
}
|
||||
@@ -175,9 +175,16 @@ Return type: void
|
||||
void CustomerMenu::selectService()
|
||||
{
|
||||
std::string vehicleNumber, vehicleBrand, vehicleModel;
|
||||
auto services = m_controller.getServices();
|
||||
util::Vector<std::string> selectedServices;
|
||||
util::clear();
|
||||
std::cout << "Select a Service\n";
|
||||
auto services = m_controller.getServices();
|
||||
if (services.isEmpty())
|
||||
{
|
||||
std::cout << "No services available!";
|
||||
util::pressEnter();
|
||||
return;
|
||||
}
|
||||
util::Vector<std::string> selectedServices;
|
||||
const Service* selectedService = selectServiceFromServices(services);
|
||||
if (selectedService == nullptr)
|
||||
{
|
||||
@@ -187,6 +194,7 @@ void CustomerMenu::selectService()
|
||||
}
|
||||
selectedServices.push_back(selectedService->getId());
|
||||
util::clear();
|
||||
std::cout << "Enter Vehicle Details\n";
|
||||
std::cout << "Enter vehicle number: ";
|
||||
util::read(vehicleNumber);
|
||||
std::cout << "Enter vehicle brand: ";
|
||||
@@ -208,8 +216,15 @@ Return type: void
|
||||
void CustomerMenu::selectComboPackage()
|
||||
{
|
||||
std::string vehicleNumber, vehicleBrand, vehicleModel;
|
||||
auto comboPackages = m_controller.getComboPackages();
|
||||
util::clear();
|
||||
std::cout << "Select a Combo Package\n";
|
||||
auto comboPackages = m_controller.getComboPackages();
|
||||
if (comboPackages.isEmpty())
|
||||
{
|
||||
std::cout << "No combo packages available!";
|
||||
util::pressEnter();
|
||||
return;
|
||||
}
|
||||
const ComboPackage* selectedComboPackage = selectComboPackageFromPackages(comboPackages);
|
||||
if (selectedComboPackage == nullptr)
|
||||
{
|
||||
|
||||
@@ -77,7 +77,7 @@ inline std::string selectServicesToRemove(util::Map<std::string, const Service*>
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Invalid choice." << std::endl;
|
||||
std::cout << "Invalid index." << std::endl;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ Parameters:
|
||||
Returns:
|
||||
- void
|
||||
*/
|
||||
static void selectInventoryItems(util::Map<std::string, const InventoryItem*>& currentInventoryItems, util::Vector<std::string>& selectedInventoryItems)
|
||||
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;
|
||||
@@ -166,7 +166,7 @@ Parameters:
|
||||
Returns:
|
||||
- bool: True if pending services exist, False otherwise
|
||||
*/
|
||||
static bool listServiceBookings(util::Map<std::string, const ServiceBooking*>& currentBookings, int& bookingsSize, util::Map<int, const ServiceBooking*>& serviceBookingsMap)
|
||||
inline bool listServiceBookings(util::Map<std::string, const ServiceBooking*>& currentBookings, int& bookingsSize, util::Map<int, const ServiceBooking*>& serviceBookingsMap)
|
||||
{
|
||||
int currentIndex = 1;
|
||||
bool hasPendingService = false;
|
||||
@@ -220,7 +220,7 @@ Parameters:
|
||||
Returns:
|
||||
- const ServiceBooking*: Pointer to the selected booking, or nullptr if invalid
|
||||
*/
|
||||
static const ServiceBooking* selectPendingServiceBookings(util::Map<int, const ServiceBooking*>& serviceBookingsMap)
|
||||
inline const ServiceBooking* selectPendingServiceBookings(util::Map<int, const ServiceBooking*>& serviceBookingsMap)
|
||||
{
|
||||
int userInputIndex;
|
||||
std::cout << "Enter a valid service index: ";
|
||||
@@ -246,7 +246,7 @@ Parameters:
|
||||
Returns:
|
||||
- void
|
||||
*/
|
||||
static void listAvailableTechnicians(util::Map<std::string, const User*> currentAvailableTechnicians, int numberOfTechnicians, util::Map<int, const User*>& currentAvailableTechniciansMap)
|
||||
inline void listAvailableTechnicians(util::Map<std::string, const User*> currentAvailableTechnicians, int numberOfTechnicians, util::Map<int, const User*>& currentAvailableTechniciansMap)
|
||||
{
|
||||
bool hasTechnicians = false;
|
||||
int currentIndex = 1;
|
||||
@@ -284,7 +284,7 @@ Parameters:
|
||||
Returns:
|
||||
- const User*: Pointer to the selected technician, or nullptr if invalid
|
||||
*/
|
||||
static const User* selectTechnician(util::Map<int, const User*>& currentAvailableTechniciansMap)
|
||||
inline const User* selectTechnician(util::Map<int, const User*>& currentAvailableTechniciansMap)
|
||||
{
|
||||
int userInputIndex;
|
||||
util::read(userInputIndex);
|
||||
@@ -307,7 +307,7 @@ Parameters:
|
||||
Returns:
|
||||
- std::string: ID of the selected invoice, or empty string if none selected
|
||||
*/
|
||||
static std::string selectInvoiceFromUserForPayment(const util::Map<std::string, const Invoice*>& currentInvoices)
|
||||
inline std::string selectInvoiceFromUserForPayment(const util::Map<std::string, const Invoice*>& currentInvoices)
|
||||
{
|
||||
int currentIndex = 1, choice;
|
||||
util::Map<int, const Invoice*> pendingInvoicesForPayment;
|
||||
@@ -359,7 +359,7 @@ static std::string selectInvoiceFromUserForPayment(const util::Map<std::string,
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Invalid choice.\n";
|
||||
std::cout << "Invalid index.\n";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -372,7 +372,7 @@ Parameters:
|
||||
Returns:
|
||||
- util::PaymentMode: Selected payment mode
|
||||
*/
|
||||
static util::PaymentMode selectPaymentMode()
|
||||
inline util::PaymentMode selectPaymentMode()
|
||||
{
|
||||
int choice;
|
||||
std::cout << "Enter the payment Mode\n1.OFFLINE\n2.ONLINE\nChoice: ";
|
||||
@@ -389,7 +389,7 @@ static util::PaymentMode selectPaymentMode()
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Invalid choice, Offline mode selected.\n";
|
||||
std::cout << "Invalid choice. Offline mode selected.\n";
|
||||
return util::PaymentMode::OFFLINE;
|
||||
}
|
||||
}
|
||||
@@ -405,7 +405,7 @@ Returns:
|
||||
Throws:
|
||||
- std::runtime_error if a null invoice is encountered
|
||||
*/
|
||||
static void displayInvoices(util::Map<std::string, const Invoice*> currentUserInvoices)
|
||||
inline void displayInvoices(util::Map<std::string, const Invoice*> currentUserInvoices)
|
||||
{
|
||||
if (currentUserInvoices.getSize() == 0)
|
||||
{
|
||||
@@ -471,7 +471,7 @@ Parameters:
|
||||
Returns:
|
||||
- std::string: ID of the selected job card, or empty string if none selected
|
||||
*/
|
||||
static std::string selectJobCardToComplete(util::Map<std::string, const JobCard*>& assignedJobCards, util::Map<int, const JobCard*>& incompleteJobCards)
|
||||
inline std::string selectJobCardToComplete(util::Map<std::string, const JobCard*>& assignedJobCards, util::Map<int, const JobCard*>& incompleteJobCards)
|
||||
{
|
||||
int currentIndex = 1;
|
||||
int choice;
|
||||
@@ -513,7 +513,7 @@ static std::string selectJobCardToComplete(util::Map<std::string, const JobCard*
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Invalid choice.\n";
|
||||
std::cout << "Invalid index.\n";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -561,7 +561,7 @@ inline const Notification* selectNotification(const util::Vector<const Notificat
|
||||
util::read(selectedIndex);
|
||||
if (!indexedNotifications.containsKey(selectedIndex))
|
||||
{
|
||||
std::cout << "Invalid selection." << std::endl;
|
||||
std::cout << "Invalid index." << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
return indexedNotifications[selectedIndex];
|
||||
@@ -623,10 +623,16 @@ Return type: void
|
||||
inline void changePasswordHelper(Controller& controller)
|
||||
{
|
||||
util::clear();
|
||||
std::string newPassword;
|
||||
const User* authenticatedUser = controller.getAuthenticatedUser();
|
||||
if (!authenticatedUser)
|
||||
{
|
||||
throw std::runtime_error("No user is currently logged in!");
|
||||
}
|
||||
std::string newPassword, confirmedPassword;
|
||||
while (true)
|
||||
{
|
||||
util::clear();
|
||||
std::cout << "Change Password\n";
|
||||
std::cout << "Enter new password: ";
|
||||
util::read(newPassword);
|
||||
if (!util::isPasswordValid(newPassword))
|
||||
@@ -635,6 +641,20 @@ inline void changePasswordHelper(Controller& controller)
|
||||
util::pressEnter();
|
||||
continue;
|
||||
}
|
||||
if (newPassword == authenticatedUser->getPassword())
|
||||
{
|
||||
std::cout << "New password cannot be same as old password. Try again\n";
|
||||
util::pressEnter();
|
||||
continue;
|
||||
}
|
||||
std::cout << "Confirm new password: ";
|
||||
util::read(confirmedPassword);
|
||||
if (confirmedPassword != newPassword)
|
||||
{
|
||||
std::cout << "Passwords are different. Try again\n";
|
||||
util::pressEnter();
|
||||
continue;
|
||||
}
|
||||
controller.changePassword(newPassword);
|
||||
std::cout << "Password changed successfully\n";
|
||||
util::pressEnter();
|
||||
@@ -764,8 +784,8 @@ inline const ComboPackage* selectComboPackageFromPackages(const util::Map<std::s
|
||||
int userInputIndex;
|
||||
std::cout << std::left
|
||||
<< std::setw(10) << "Index"
|
||||
<< std::setw(15) << "Combo Package ID"
|
||||
<< std::setw(15) << "Combo Package Name"
|
||||
<< std::setw(15) << "Combo ID"
|
||||
<< std::setw(15) << "Combo Name"
|
||||
<< std::setw(15) << "Estimate Cost"
|
||||
<< std::endl;
|
||||
for (int index = 0; index < comboPackages.getSize(); index++)
|
||||
|
||||
@@ -85,6 +85,7 @@ void UserInterface::login()
|
||||
{
|
||||
std::string username, password;
|
||||
util::clear();
|
||||
std::cout << "Login\n";
|
||||
std::cout << "Enter username: ";
|
||||
util::read(username);
|
||||
std::cout << "Enter password: ";
|
||||
@@ -92,7 +93,7 @@ void UserInterface::login()
|
||||
if (m_controller.login(username, password))
|
||||
{
|
||||
const User* authenticatedUser = m_controller.getAuthenticatedUser();
|
||||
if (authenticatedUser != nullptr)
|
||||
if (authenticatedUser && authenticatedUser->getState() != util::State::INACTIVE)
|
||||
{
|
||||
switch (authenticatedUser->getUserType())
|
||||
{
|
||||
@@ -110,10 +111,16 @@ void UserInterface::login()
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (authenticatedUser && authenticatedUser->getState() == util::State::INACTIVE)
|
||||
{
|
||||
std::cout << "\nError: Your account has been disabled. Please contact your Administrator.";
|
||||
util::pressEnter();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "\nError: Invalid Username or Password";
|
||||
util::pressEnter();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +136,7 @@ void UserInterface::registerCustomer()
|
||||
{
|
||||
std::string username, name, email, phone, password;
|
||||
util::clear();
|
||||
std::cout << "Register Customer\n";
|
||||
std::cout << "Enter username: ";
|
||||
util::read(username);
|
||||
std::cout << "Enter name: ";
|
||||
|
||||
Reference in New Issue
Block a user