Compare commits

..

43 Commits

Author SHA1 Message Date
joelthomastrenser b1b6125d88 Fix: create missing directories before file creation
Changes:
- Added ensureDirectoryExists() helper using _mkdir()
- Automatically create missing directories before file operations
- Added FileHelper include in FileManager
- Removed placeholder files/README.md

Fixes #1793
2026-05-28 10:32:36 +05:30
joelthomastrenser e739ec6ee2 Merged PR 1134: Fix Invoice Deserialization Failure for PaymentMode::NOTSET
Changes:

- Added support for PaymentMode::NOTSET in payment mode string-to-enum conversion.
- Improved invoice table column labels and spacing for better readability in invoice display screens.

Fixes #1789

Related work items: #1646, #1789
2026-05-27 19:51:59 +05:30
joelthomastrenser f78e02ed3d Fix: handle NOTSET payment mode during invoice deserialization
Changes:

- Added support for PaymentMode::NOTSET in string-to-enum conversion
  to prevent invoice deserialization failures during system startup
- Improved invoice table column labels and spacing for better readability
  and alignment in invoice display screens

Fixes #1789
2026-05-27 18:47:49 +05:30
Avinash Rajesh 807490443e Merged PR 1128: Fix customer removal flow, admin inventory UI issues, and user display improvements
Changes:

- Refactored customer and technician removal flow:
  - Ensured linked job cards and service bookings are properly cancelled.
  - Centralized cancellation logic with processBookingCancellation helper for consistent notifications, technician reassignment, and inventory restoration.
  - Prevented duplicate inventory restocking during customer removal.
  - Preserved customer references and IDs correctly during booking cancellation.

- Admin inventory management improvements:
  - Added heading output to Remove Inventory Item submenu for clearer context.
  - Prevented service creation with empty inventory selection by adding validation and early return.
  - Improved combo package creation by simplifying null checks with modern idioms.
  - Enhanced inventory selection flow:
    - Clear screen and context header for better UX.
    - Prevent duplicate item selection by checking already chosen items.
    - Changed exit option from -1 to 0 for consistency.
    - Added pressEnter prompt after successful item addition.
  - Simplified pointer checks using concise conditions (e.g., if (item)).

- User management UI fix:
  - Updated active user display in Remove User menu to include Full Name column, ensuring administrators have complete visibility when managing users.

Fixes #1788
Fixes #1778
Fixes #1781

Related work items: #1778, #1781, #1788
2026-05-27 18:38:17 +05:30
Avinash Rajesh b7bc1f574d Implement Review Fixes 2026-05-27 17:32:36 +05:30
Avinash Rajesh b25b3d59cf Fix Admin Menu Remove User Incomplete Data
- Updated active user display to include Full Name column.
- Ensured both header and row output show full name alongside ID, username, and user type.
- Improved clarity of Remove User submenu by presenting complete user information.

Fixes #1788
2026-05-27 17:12:23 +05:30
Avinash Rajesh c1bd2a6ef1 Fix Admin Inventory Management UI Issues and Workflow Enhancements
- Added heading output to Remove Inventory Item submenu for clearer user context.
- Prevented service creation with empty inventory selection by adding validation and early return.
- Improved combo package creation by simplifying null checks with modern idioms.
- Enhanced inventory selection flow:
  - Clear screen and context header for better UX.
  - Prevent duplicate item selection by checking already chosen items.
  - Changed exit option from -1 to 0 for consistency.
  - Added pressEnter prompt after successful item addition.
- Simplified pointer checks using concise conditions (e.g., if (item)).

Fixes #1778
2026-05-27 17:12:23 +05:30
Avinash Rajesh 859f7bbeaa Fix Customer Removal and Cancellation Handling Issues
- Refactored customer and technician removal flow to ensure linked job cards and service bookings are properly cancelled.
- Added inventory restoration logic to avoid duplicate restocking when cancelling bookings.
- Introduced processBookingCancellation helper for consistent cancellation handling, notifications, and technician reassignment.
- Updated UserManagementService::removeUser to invoke appropriate cancellation routines based on user type.
- Ensured customer references and IDs are preserved correctly during booking cancellation.

Fixes #1781
2026-05-27 17:12:20 +05:30
Jissin Mathew d6cc6fc04f Merged PR 1129: Fix view invoices, combo packages, and technician menu UI issues
Changes
- Added tabular invoice list with selection before full details
- Displayed Payment Mode in detailed invoice view
- Removed duplicate pressEnter() calls and improved console messages
- Changed combo package bookings to start with PENDING status
- Added filterComboPackages to list only active packages
- Improved CustomerMenu combo package selection with clearer messages and formatting
- Enhanced output with console clearing, spacing, and success feedback
- Added truncateString utility for consistent display of long names
- Updated filter functions to use reference parameters for efficiency
- Cleared console before displaying Complete Job screen
- Improved submenu header and formatting for clarity
- Prevented table headers from showing when no jobs exist
- Refined job completion flow with consistent messages

Fixes #1779
Fixes #1782
Fixes #1784

Related work items: #1779, #1782, #1784
2026-05-27 17:09:18 +05:30
Jissin Mathew 62cd7dcc89 Implement Review Fixes 2026-05-27 17:09:03 +05:30
Jissin Mathew c67bf962c1 Fix Customer Menu View Invoices UI and Navigation Issues
- Added tabular invoice list for better navigation
- Enabled user to select an invoice before viewing full details
- Displayed Payment Mode in detailed invoice view
- Removed duplicate pressEnter() calls
- Improved console messages and formatting for clarity

Fixes #1784
2026-05-27 16:55:14 +05:30
Jissin Mathew efd8b32a5f Fix Combo Package Status and Creation Flow Issues
- Changed combo package bookings to start with PENDING status
- Added filterComboPackages to list only active packages
- Improved CustomerMenu combo package selection with clearer messages and formatting
- Enhanced output with console clearing, spacing, and success feedback
- Added truncateString utility for consistent display of long names
- Updated filter functions to use reference parameters for efficiency

Fixes #1779
2026-05-27 16:53:30 +05:30
Jissin Mathew 01596e825e Fix Technician Menu Complete Job UI Issues
- Cleared console before displaying Complete Job screen
- Improved submenu header and formatting for clarity
- Prevented table headers from showing when no jobs exist
- Refined job completion flow with consistent messages

Fixes #1782
2026-05-27 16:53:29 +05:30
joelthomastrenser fb50aeba47 Merged PR 1127: Fix notification UI formatting, payment flow issues, and observer cleanup
Changes:

- Fix notification table alignment issues in admin notification screens
- Add proper spacing for notification title column display
- Remove unnecessary tab spacing from Configure Notification Preferences heading
- Change ServiceBooking ID prefix to avoid conflict with Service IDs
- Remove unnecessary newline characters from service booking completion notifications
- Detach removed users from all service observer lists during user removal
- Fix Complete Payments screen clearing immediately after heading display
- Improve table spacing in Complete Payments screen
- Prevent invalid payment mode selection from defaulting to OFFLINE mode

Fixes #1780
Fixes #1783
Fixes #1777
Fixes #1786

Related work items: #1777, #1780, #1783, #1786
2026-05-27 16:49:20 +05:30
joelthomastrenser defee9aa15 Fix ServiceBooking ID prefix and notification messages
- change ServiceBooking ID prefix from `SRV` to `SBK`
- add customer notification when technician is assigned to booking
- fix typo in job completion exception message
- remove unnecessary newline characters from completion notifications

Fixes #1780
2026-05-27 16:02:23 +05:30
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
joelthomastrenser 3d7944f77d Impelement review fixes 2026-05-26 20:12:57 +05:30
joelthomastrenser 9439202c5a Fix: improve combo package selection UI and empty state handling
- add Select a Combo Package header
- show message when no combo packages are available
- improve combo package table header labels

Fixes #1743
2026-05-26 18:04:05 +05:30
joelthomastrenser e6faa63b88 Fix: improve customer service selection flow and booking status
- Set new service bookings to PENDING instead of STARTED
- Show message when no services are available
- Add Select a Service header
- Add Enter Vehicle Details header
- Improve service selection screen flow

Fixes #1753
2026-05-26 17:46:11 +05:30
joelthomastrenser 31e660bc9e Fix: add duplicate user validation and update register customer UI
- add username duplicate validation
- add email duplicate validation
- add phone duplicate validation
- move duplicate checks to Validator utility
- add Register Customer header
- update registration success message

Fixes #1737
2026-05-26 16:47:11 +05:30
joelthomastrenser 5fd0a47459 Fix off-by-one error in sendLowStockAlerts loop
Fixes #1751
2026-05-26 14:22:41 +05:30
joelthomastrenser fb509ccb3f Change static functions to inline in MenuHelper
Fixes #1754
2026-05-26 12:40:27 +05:30
joelthomastrenser 8d07b11eae Fix login validation and improve error messages
- Check if user account is inactive before allowing login
- Add password confirmation in change password dialog
- Prevent changing password to the same value
- Standardize error messages to say "Invalid index" instead of "Invalid choice"
- Add blank line before system pause prompt
- Add "Change Password" header to change password screen
- Show error message when login fails

Fixes #1738
Fixes #1736
2026-05-26 12:18:38 +05:30
Jissin Mathew 1e11903cca Merged PR 1069: fix: cleanup and small improvements
fix: cleanup and small improvements

- added empty check in inventory alerts
- corrected parts cost calculation
- removed unused getUser method
- updated default admin password
- fixed missing includes in menus
- cleaned up MenuHelper comments
2026-05-25 20:15:26 +05:30
Jissin Mathew 089fe496b2 Merged PR 1042: Service-Management-1553, Payment-Management-1598
**Service Management**
**SER001 - Assign Job to Technician:** Allows admins to assign service jobs to technicians for efficient work distribution.
**SER002 - Create Service:** Allows admins to create new services that customers can select and book.
**SER003 - Remove Service:** Allows admins to remove outdated services from the system.
**SER004 - View Service History:** Allows customers to view the status and history of their booked services.
**SER005 - Update Service Status:** Allows technicians to update service progress so customers can track it.

**Payment Management**
**PAY001 - Generate Invoice:** Automatically creates invoices with service, parts, and labor cost details for customer payments.
**PAY002 - Complete Payments:** Allows customers to complete pending payments and receive confirmation.
**PAY003 - View Invoices:** Allows customers to view invoice details for their purchased services.

Related work items: #1553, #1577, #1578, #1579, #1580, #1581, #1598, #1599, #1600, #1601, #1655, #1656, #1679, #1680
2026-05-25 20:07:59 +05:30
19 changed files with 1101 additions and 462 deletions
@@ -409,10 +409,8 @@ void Controller::removeUser(const std::string& userID)
User* user = m_userManagementService.getUser(userID); User* user = m_userManagementService.getUser(userID);
if (!user) if (!user)
{ {
throw std::runtime_error("Error User not Found.\n"); throw std::runtime_error("Error: User not Found.\n");
} }
m_serviceManagementService.cancelCustomerServiceBookings(userID);
m_serviceManagementService.cancelTechnicianJobs(userID);
m_userManagementService.removeUser(userID); m_userManagementService.removeUser(userID);
} }
@@ -1 +0,0 @@
Place files here.
@@ -24,9 +24,10 @@ Parameters: None
Returns: A new ServiceBooking object. Returns: A new ServiceBooking object.
*/ */
ServiceBooking::ServiceBooking() ServiceBooking::ServiceBooking()
: m_id("SRV" + std::to_string(++m_uid)), : m_id("SBK" + std::to_string(++m_uid)),
m_customer(nullptr), m_customer(nullptr),
m_assignedTechnician(nullptr), m_assignedTechnician(nullptr),
m_status(util::ServiceJobStatus::PENDING),
m_discountPercentage(0.0) {} m_discountPercentage(0.0) {}
/* /*
@@ -56,7 +57,7 @@ ServiceBooking::ServiceBooking(
const std::string& vehicleModel, const std::string& vehicleModel,
double discountPercentage double discountPercentage
) )
: m_id("SRV" + std::to_string(++m_uid)), : m_id("SBK" + std::to_string(++m_uid)),
m_status(status), m_status(status),
m_services(services), m_services(services),
m_customerId(customerId), m_customerId(customerId),
@@ -80,7 +80,7 @@ void InventoryManagementService::sendLowStockAlerts()
{ {
throw std::runtime_error("The system has no admins present!"); 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); InventoryItem* inventoryItem = inventoryItems.getValueAt(index);
if (inventoryItem && inventoryItem->getQuantity() < config::threshold::INVENTORY_LOW_STOCK_THRESHOLD) if (inventoryItem && inventoryItem->getQuantity() < config::threshold::INVENTORY_LOW_STOCK_THRESHOLD)
@@ -323,7 +323,7 @@ void InventoryManagementService::sendNotification(User* user, const std::string&
Factory::getObject<Notification>( Factory::getObject<Notification>(
user->getId(), user->getId(),
user, user,
"InventoryManagementService: " + title, title,
message, message,
util::Timestamp() util::Timestamp()
); );
@@ -86,7 +86,7 @@ void PaymentManagementService::sendNotification(User* user, const std::string& t
Factory::getObject<Notification>( Factory::getObject<Notification>(
user->getId(), user->getId(),
user, user,
"PaymentManagementService: " + title, title,
message, message,
util::Timestamp() util::Timestamp()
); );
@@ -294,7 +294,7 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking)
{ {
throw std::runtime_error("Invoice generation failed: booking is null."); throw std::runtime_error("Invoice generation failed: booking is null.");
} }
double totalLabourCost = 0, totalPartsCost = 0, totalServiceCost = 0; double totalLaborCost = 0, totalPartsCost = 0, totalServiceCost = 0;
double discountPercentage = booking->getDiscountPercentage(); double discountPercentage = booking->getDiscountPercentage();
std::string bookingID = booking->getId(); std::string bookingID = booking->getId();
util::Map<std::string, Service*> servicesInTheBookedService = booking->getServices(); util::Map<std::string, Service*> servicesInTheBookedService = booking->getServices();
@@ -303,9 +303,10 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking)
for (int iterator = 0; iterator < currentJobCards.getSize(); iterator++) for (int iterator = 0; iterator < currentJobCards.getSize(); iterator++)
{ {
JobCard* currentJobCard = currentJobCards.getValueAt(iterator); JobCard* currentJobCard = currentJobCards.getValueAt(iterator);
if (currentJobCard->getBookingId() == bookingID && currentJobCard->getStatus() != util::ServiceJobStatus::COMPLETED) util::ServiceJobStatus currentJobCardStatus = currentJobCard->getStatus();
if (currentJobCard->getBookingId() == bookingID && currentJobCardStatus != util::ServiceJobStatus::CANCELLED && currentJobCardStatus != util::ServiceJobStatus::COMPLETED)
{ {
throw std::runtime_error("Invoice generation failed: not all job cards are completed for booking '" + bookingID + "'."); throw std::runtime_error("Invoice generation failed: Not all job cards are completed for booking '" + bookingID + "'.");
} }
} }
for (int iterator = 0; iterator < servicesInTheBookedService.getSize(); iterator++) for (int iterator = 0; iterator < servicesInTheBookedService.getSize(); iterator++)
@@ -314,13 +315,13 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking)
if (currentService) if (currentService)
{ {
createInventoryItemsMap(completeInventoryItemMapOfBooking, currentService); createInventoryItemsMap(completeInventoryItemMapOfBooking, currentService);
totalLabourCost += currentService->getLaborCost(); totalLaborCost += currentService->getLaborCost();
totalPartsCost += util::calculatePartsCost(currentService); totalPartsCost += util::calculatePartsCost(currentService);
} }
} }
totalServiceCost = totalLabourCost + totalPartsCost; totalServiceCost = totalLaborCost + totalPartsCost;
totalServiceCost -= (totalServiceCost * (discountPercentage / 100)); totalServiceCost -= (totalServiceCost * (discountPercentage / 100));
Invoice* invoice = Factory::getObject<Invoice>(bookingID, booking, util::Timestamp(), totalLabourCost, completeInventoryItemMapOfBooking, totalPartsCost, discountPercentage, totalServiceCost, util::Timestamp(), util::PaymentMode::NOTSET, util::PaymentStatus::PENDING); Invoice* invoice = Factory::getObject<Invoice>(bookingID, booking, util::Timestamp(), totalLaborCost, completeInventoryItemMapOfBooking, totalPartsCost, discountPercentage, totalServiceCost, util::Timestamp(), util::PaymentMode::NOTSET, util::PaymentStatus::PENDING);
util::Map<std::string, Invoice*>& currentInvoices = m_dataStore.getInvoices(); util::Map<std::string, Invoice*>& currentInvoices = m_dataStore.getInvoices();
currentInvoices.insert(invoice->getId(), invoice); currentInvoices.insert(invoice->getId(), invoice);
} }
@@ -375,7 +376,7 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti
invoice->setStatus(util::PaymentStatus::COMPLETED); invoice->setStatus(util::PaymentStatus::COMPLETED);
std::string title, message; std::string title, message;
title = "Payment successful"; title = "Payment successful";
message = "Payment successful for invoice ID " + invoiceID; message = "Payment successful for Invoice ID " + invoiceID;
sendNotification(currentUser, title, message); sendNotification(currentUser, title, message);
} }
} }
@@ -23,7 +23,6 @@ Date:19-May-2026
#include "ServiceBooking.h" #include "ServiceBooking.h"
#include "ServiceManagementService.h" #include "ServiceManagementService.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "Timestamp.h"
#include "User.h" #include "User.h"
#include "UserManagementService.h" #include "UserManagementService.h"
#include "Utility.h" #include "Utility.h"
@@ -61,7 +60,7 @@ void ServiceManagementService::purchaseService(const util::Vector<std::string>&
Service* service = servicesMap.getValueAt(serviceIndex); Service* service = servicesMap.getValueAt(serviceIndex);
selectedServices[service->getId()] = service; 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) if (serviceBooking == nullptr)
{ {
throw std::runtime_error("Failed to create service booking"); throw std::runtime_error("Failed to create service booking");
@@ -100,7 +99,7 @@ void ServiceManagementService::purchaseComboPackage(const std::string& comboPack
} }
const ComboPackage* comboPackage = comboPackagesMap[comboPackageID]; const ComboPackage* comboPackage = comboPackagesMap[comboPackageID];
util::Map<std::string, Service*> selectedServices = comboPackage->getServices(); util::Map<std::string, Service*> selectedServices = comboPackage->getServices();
ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::STARTED, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, comboPackage->getDiscountPercentage()); ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, comboPackage->getDiscountPercentage());
if (serviceBooking == nullptr) if (serviceBooking == nullptr)
{ {
throw std::runtime_error("Failed to create combo package service booking"); throw std::runtime_error("Failed to create combo package service booking");
@@ -175,7 +174,7 @@ void ServiceManagementService::sendNotification(User* user, const std::string& t
Factory::getObject<Notification>( Factory::getObject<Notification>(
user->getId(), user->getId(),
user, user,
"ServiceManagementService: " + title, title,
message, message,
util::Timestamp() util::Timestamp()
); );
@@ -508,6 +507,89 @@ void ServiceManagementService::saveObservers()
util::saveObservers(config::file::SERVICEMANAGEMENTOBSERVERS, this); util::saveObservers(config::file::SERVICEMANAGEMENTOBSERVERS, this);
} }
/*
Function: restoreInventory
Description: Restores inventory quantities for all required items in the services associated
with a given booking. Each item's quantity is incremented by a fixed value.
Parameter: ServiceBooking* booking - Pointer to the booking whose inventory items need to be restored
Return type: void
*/
static void restoreInventory(ServiceBooking* booking)
{
const int INCREMENT_VALUE = 1;
if (!booking)
{
return;
}
const auto& services = booking->getServices();
for (int serviceIterator = 0; serviceIterator < services.getSize(); ++serviceIterator)
{
Service* service = services.getValueAt(serviceIterator);
if (!service)
{
continue;
}
const auto& items = service->getRequiredInventoryItems();
for (int InventoryIterator = 0; InventoryIterator < items.getSize(); ++InventoryIterator)
{
InventoryItem* item = items.getValueAt(InventoryIterator);
if (item)
{
item->setQuantity(item->getQuantity() + INCREMENT_VALUE);
}
}
}
}
/*
Function: processBookingCancellation
Description: Cancels jobs and updates the status of a given booking. Sends notifications to the
specified user, resets technician assignment if needed, and restores inventory items.
Parameter: ServiceBooking* booking - Pointer to the booking being cancelled
util::ServiceJobStatus newServiceBookingStatus - New status to assign to the booking
const std::string& notificationTitle - Title of the booking cancellation notification
const std::string& notificationMessage - Message body of the booking cancellation notification
User* notifyUser - User to notify about the cancellation
util::ServiceJobStatus jobCardStatus - New status to assign to associated job cards
const std::string& jobNotificationTitle - Title of the job cancellation notification
const std::string& jobNotificationMessage - Message body of the job cancellation notification
util::Map<std::string, JobCard*>& jobs - Collection of job cards to update
ServiceManagementService& currentService - Reference to the service for sending notifications
Return type: void
*/
static void processBookingCancellation(ServiceBooking* booking,
util::ServiceJobStatus newServiceBookingStatus,
const std::string& notificationTitle,
const std::string& notificationMessage,
User* notifyUser,
util::ServiceJobStatus jobCardStatus,
const std::string& jobNotificationTitle,
const std::string& jobNotificationMessage,
util::Map<std::string, JobCard*>& jobs, ServiceManagementService& currentService)
{
if (!booking || !notifyUser)
{
return;
}
for (int jobIterator = 0; jobIterator < jobs.getSize(); ++jobIterator)
{
JobCard* jobCard = jobs.getValueAt(jobIterator);
if (jobCard && jobCard->getBookingId() == booking->getId())
{
jobCard->setStatus(jobCardStatus);
currentService.sendNotification(notifyUser, jobNotificationTitle, jobNotificationMessage);
}
}
booking->setStatus(newServiceBookingStatus);
currentService.sendNotification(notifyUser, notificationTitle, notificationMessage);
if (newServiceBookingStatus == util::ServiceJobStatus::PENDING)
{
booking->setAssignedTechnician(nullptr);
booking->setAssignedTechnicianId("");
}
restoreInventory(booking);
}
/* /*
Function: cancelCustomerServiceBookings Function: cancelCustomerServiceBookings
Description: Cancels all service bookings associated with a given customer or technician. Description: Cancels all service bookings associated with a given customer or technician.
@@ -516,69 +598,47 @@ Description: Cancels all service bookings associated with a given customer or te
Parameter: const std::string& userID - ID of the customer or technician Parameter: const std::string& userID - ID of the customer or technician
Return type: void Return type: void
*/ */
void ServiceManagementService::cancelCustomerServiceBookings(const std::string& userID) void ServiceManagementService::cancelCustomerServiceBookings(const std::string& customerID)
{ {
const int INCREMENT_VALUE = 1;
auto& users = m_dataStore.getUsers(); auto& users = m_dataStore.getUsers();
int userIndex = users.find(userID); int userIndex = users.find(customerID);
if (userIndex == -1) if (userIndex == -1)
{ {
throw std::runtime_error("User not found: " + userID); throw std::runtime_error("User not found: " + customerID);
} }
User* user = users.getValueAt(userIndex); User* customer = users.getValueAt(userIndex);
if (user == nullptr) if (!customer)
{ {
throw std::runtime_error("User not found: " + userID); throw std::runtime_error("User not found: " + customerID);
} }
util::UserType type = user->getUserType(); auto& bookings = m_dataStore.getServiceBookings();
auto& bookings = DataStore::getInstance().getServiceBookings(); auto& jobs = m_dataStore.getJobCards();
for (int bookingIterator = 0; bookingIterator < bookings.getSize(); bookingIterator++) for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++)
{ {
ServiceBooking* booking = bookings.getValueAt(bookingIterator); ServiceBooking* booking = bookings.getValueAt(iteratorOne);
if (booking != nullptr && if (!booking)
(booking->getCustomerId() == userID || booking->getAssignedTechnicianId() == userID))
{ {
if (booking->getStatus() == util::ServiceJobStatus::PENDING || continue;
booking->getStatus() == util::ServiceJobStatus::STARTED) }
if (booking->getCustomerId() != customerID)
{ {
if (type == util::UserType::CUSTOMER) continue;
}
if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED)
{ {
booking->setStatus(util::ServiceJobStatus::CANCELLED); continue;
booking->setCustomer(nullptr); }
booking->setCustomerId("");
User* assignedTechnician = booking->getAssignedTechnician(); User* assignedTechnician = booking->getAssignedTechnician();
std::string title = "Customer Service Cancelled"; std::string titleToTechnician = "Customer Service Cancelled";
std::string message = "The customer has cancelled their service booking. Your assigned job card has been cancelled and the inventory has been restocked."; std::string messageToTechnician = "The customer has cancelled their service booking. Your assigned job card has been cancelled and the inventory has been restocked.";
sendNotification(assignedTechnician, title, message); std::string jobTitle = "Job Cancelled";
} std::string jobMessage = "The job has been cancelled. Your job card has been cancelled and the inventory has been restocked.";
else if (type == util::UserType::TECHNICIAN) processBookingCancellation(booking,
{ util::ServiceJobStatus::CANCELLED,
booking->setStatus(util::ServiceJobStatus::PENDING); titleToTechnician, messageToTechnician, assignedTechnician,
std::string title = "Technician Unavailable"; util::ServiceJobStatus::CANCELLED,
std::string message = "Your assigned technician is no longer available. Your booking has been reset to pending, and we will reassign a new technician shortly."; jobTitle, jobMessage, jobs, *this
sendNotification(booking->getCustomer(), title, message); );
}
booking->setAssignedTechnician(nullptr);
booking->setAssignedTechnicianId("");
const auto& ListOfServices = booking->getServices();
for (int serviceIterator = 0; serviceIterator < ListOfServices.getSize(); serviceIterator++)
{
Service* service = ListOfServices.getValueAt(serviceIterator);
if (service != nullptr)
{
const auto& items = service->getRequiredInventoryItems();
for (int itemIterator = 0; itemIterator < items.getSize(); itemIterator++)
{
InventoryItem* item = items.getValueAt(itemIterator);
if (item != nullptr)
{
item->setQuantity(item->getQuantity() + INCREMENT_VALUE);
}
}
}
}
}
}
} }
} }
@@ -591,34 +651,48 @@ Return type: void
*/ */
void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID) void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID)
{ {
const int INCREMENT_VALUE = 1; auto& users = m_dataStore.getUsers();
int userIndex = users.find(technicianID);
if (userIndex == -1)
{
throw std::runtime_error("User not found: " + technicianID);
}
User* technician = users.getValueAt(userIndex);
if (!technician)
{
throw std::runtime_error("User not found: " + technicianID);
}
auto& bookings = m_dataStore.getServiceBookings();
auto& jobs = m_dataStore.getJobCards(); auto& jobs = m_dataStore.getJobCards();
for (int jobIterator = 0; jobIterator < jobs.getSize(); jobIterator++) for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++)
{ {
JobCard* job = jobs.getValueAt(jobIterator); ServiceBooking* booking = bookings.getValueAt(iteratorOne);
if (job != nullptr && job->getTechnicianId() == technicianID) if (!booking)
{ {
if (job->getStatus() == util::ServiceJobStatus::PENDING || job->getStatus() == util::ServiceJobStatus::STARTED) continue;
{
job->setStatus(util::ServiceJobStatus::CANCELLED);
std::string title = "Job Cancelled";
std::string message = "The Job has cancelled. Your job card has been cancelled and the inventory has been restocked.";
sendNotification(job->getTechnician(), title, message);
Service* service = job->getService();
if (service != nullptr)
{
const auto& items = service->getRequiredInventoryItems();
for (int itemIterator = 0; itemIterator < items.getSize(); itemIterator++)
{
InventoryItem* item = items.getValueAt(itemIterator);
if (item != nullptr)
{
item->setQuantity(item->getQuantity() + INCREMENT_VALUE);
}
} }
std::string technicianId = booking->getAssignedTechnicianId();
if (technicianId != technicianID)
{
continue;
} }
if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED)
{
continue;
} }
User* customer = booking->getCustomer();
if (!customer)
{
continue;
} }
std::string title = "Technician Unavailable";
std::string message = "Your assigned technician is no longer available. Your booking has been reset to pending and we will reassign a new technician shortly.";
processBookingCancellation(booking,
util::ServiceJobStatus::PENDING,
title, message, customer,
util::ServiceJobStatus::CANCELLED,
title, message, jobs, *this
);
} }
} }
@@ -817,6 +891,10 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const
} }
currentBooking->setAssignedTechnician(selectedTechnician); currentBooking->setAssignedTechnician(selectedTechnician);
currentBooking->setAssignedTechnicianId(selectedTechnician->getId()); currentBooking->setAssignedTechnicianId(selectedTechnician->getId());
if (currentBooking->getStatus() == util::ServiceJobStatus::PENDING)
{
currentBooking->setStatus(util::ServiceJobStatus::STARTED);
}
std::string title = "Job card created"; std::string title = "Job card created";
std::string message = "Job card created for the service and you are assigned for that."; 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()); JobCard* jobCard = Factory::getObject<JobCard>(bookingID, currentBooking, currentService, serviceID, technicianID, selectedTechnician, util::Timestamp(), util::ServiceJobStatus::STARTED, util::Timestamp());
@@ -829,6 +907,9 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const
{ {
throw std::runtime_error("Failed to create job card."); throw std::runtime_error("Failed to create job card.");
} }
title = "Technician assigned";
message = "A technician has been assigned to your Service Booking with ID " + bookingID;
sendNotification(currentBooking->getCustomer(), title, message);
} }
/* /*
@@ -906,9 +987,27 @@ Throws:
void ServiceManagementService::removeService(const std::string& serviceID) void ServiceManagementService::removeService(const std::string& serviceID)
{ {
util::Map<std::string, Service*>& currentServices = m_dataStore.getServices(); util::Map<std::string, Service*>& currentServices = m_dataStore.getServices();
util::Map<std::string, ComboPackage*>& currentComboPackages = m_dataStore.getComboPackages();
if (currentServices.find(serviceID) != -1) if (currentServices.find(serviceID) != -1)
{ {
currentServices.getValueAt(currentServices.find(serviceID))->setState(util::State::INACTIVE); 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 else
{ {
@@ -1033,11 +1132,11 @@ void ServiceManagementService::completeJob(const std::string& jobID)
} }
else else
{ {
throw std::runtime_error("Failed to complete the job, some error occured or job already completed."); throw std::runtime_error("Failed to complete the job, some error occurred or job already completed.");
} }
if (!jobStatusUpdated) if (!jobStatusUpdated)
{ {
throw std::runtime_error("Failed to complete the job, some error occured or job already completed."); throw std::runtime_error("Failed to complete the job, some error occurred or job already completed.");
} }
serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs); serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs);
@@ -1045,8 +1144,8 @@ void ServiceManagementService::completeJob(const std::string& jobID)
{ {
currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED); currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED);
paymentManagementService.generateInvoice(currentJob->getBooking()); paymentManagementService.generateInvoice(currentJob->getBooking());
std::string title = "Service Booking completed,Invoice Generated.\n"; std::string title = "Service Booking completed. Invoice Generated.";
std::string message = "Services completed for the booking and invoice generated.\n"; std::string message = "Services completed for the booking and invoice generated.";
sendNotification(currentJob->getBooking()->getCustomer(), title, message); sendNotification(currentJob->getBooking()->getCustomer(), title, message);
} }
} }
@@ -19,7 +19,7 @@ Date:19-May-2026
#include "User.h" #include "User.h"
#include "UserManagementService.h" #include "UserManagementService.h"
#include "Vector.h" #include "Vector.h"
#include "Validator.h"
/* /*
Function: ensureAdminExists Function: ensureAdminExists
@@ -74,16 +74,18 @@ void UserManagementService::createUser(const std::string& username, const std::s
PaymentManagementService paymentManagementService; PaymentManagementService paymentManagementService;
ServiceManagementService serviceManagementService; ServiceManagementService serviceManagementService;
auto& usersMap = m_dataStore.getUsers(); auto& usersMap = m_dataStore.getUsers();
int index = usersMap.findIf( if (util::isUsernameDuplicate(username, usersMap))
[&](const std::string&, User* user)
{
return user->getUserName() == username;
}
);
if (index != -1)
{ {
throw std::runtime_error("Username already exists"); 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); User* newUser = Factory::getObject<User>(username, password, name, phone, email, type);
usersMap.insert(newUser->getId(), newUser); usersMap.insert(newUser->getId(), newUser);
paymentManagementService.attach(newUser); paymentManagementService.attach(newUser);
@@ -109,9 +111,23 @@ void UserManagementService::updateUserDetails(const std::string& userID, const s
int index = usersMap.find(userID); int index = usersMap.find(userID);
if (index == -1) 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); 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->setEmail(email);
user->setPhone(phone); user->setPhone(phone);
} }
@@ -281,13 +297,27 @@ Return type: void
*/ */
void UserManagementService::removeUser(const std::string& userID) void UserManagementService::removeUser(const std::string& userID)
{ {
InventoryManagementService inventoryManagementService;
PaymentManagementService paymentManagementService;
ServiceManagementService serviceManagementService;
int index = m_dataStore.getUsers().find(userID); int index = m_dataStore.getUsers().find(userID);
if (index != -1) if (index != -1)
{ {
User* user = m_dataStore.getUsers().getValueAt(index); User* user = m_dataStore.getUsers().getValueAt(index);
if (user != nullptr) if (user != nullptr)
{ {
if (user->getUserType() == util::UserType::CUSTOMER)
{
serviceManagementService.cancelCustomerServiceBookings(userID);
}
if (user->getUserType() == util::UserType::TECHNICIAN)
{
serviceManagementService.cancelTechnicianJobs(userID);
}
user->setState(util::State::INACTIVE); user->setState(util::State::INACTIVE);
inventoryManagementService.detach(user);
paymentManagementService.detach(user);
serviceManagementService.detach(user);
} }
} }
} }
@@ -299,7 +329,7 @@ util::Map<std::string, User*> UserManagementService::getUsers(util::UserType typ
for (int iterator = 0; iterator < currentUsers.getSize(); iterator++) for (int iterator = 0; iterator < currentUsers.getSize(); iterator++)
{ {
User* currentUser = currentUsers.getValueAt(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); filteredUsersMap.insert(currentUser->getId(), currentUser);
} }
@@ -137,6 +137,10 @@ namespace util
{ {
return PaymentMode::OFFLINE; return PaymentMode::OFFLINE;
} }
if (value == "NOTSET")
{
return PaymentMode::NOTSET;
}
throw std::invalid_argument("Invalid PaymentMode string"); throw std::invalid_argument("Invalid PaymentMode string");
} }
@@ -11,10 +11,39 @@ Date: 22-May-2026
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
#include <direct.h>
#include "Vector.h" #include "Vector.h"
namespace util namespace util
{ {
/*
Function: ensureDirectoryExists
Description: Creates all missing directories present in the given file path.
Iteratively parses the path and creates each directory level
using _mkdir() before file operations are performed.
Parameters:
- filePath: const std::string&, relative or absolute file path
Returns:
- void
Throws:
- None (_mkdir failures are intentionally ignored if directory already exists)
*/
inline void ensureDirectoryExists(const std::string& filePath)
{
size_t position = 0;
while ((position = filePath.find('/', position)) != std::string::npos)
{
std::string directory = filePath.substr(0, position);
if (!directory.empty())
{
(void)_mkdir(directory.c_str());
}
position++;
}
}
/* /*
Function: loadRecords Function: loadRecords
Description: Loads records from a given file path into a vector of strings. Description: Loads records from a given file path into a vector of strings.
@@ -32,6 +61,7 @@ namespace util
std::ifstream file(filePath); std::ifstream file(filePath);
if (!file.is_open()) if (!file.is_open())
{ {
ensureDirectoryExists(filePath);
std::ofstream newFile(filePath); std::ofstream newFile(filePath);
newFile.close(); newFile.close();
file.open(filePath); file.open(filePath);
@@ -15,6 +15,7 @@ Date: 22-May-2026
#include <fstream> #include <fstream>
#include "Vector.h" #include "Vector.h"
#include "Map.h" #include "Map.h"
#include "FileHelper.h"
namespace util namespace util
{ {
@@ -51,6 +52,7 @@ namespace util
std::ifstream file(m_filePath); std::ifstream file(m_filePath);
if (!file.is_open()) if (!file.is_open())
{ {
ensureDirectoryExists(m_filePath);
std::ofstream newFile(m_filePath); std::ofstream newFile(m_filePath);
newFile.close(); newFile.close();
file.open(m_filePath); file.open(m_filePath);
@@ -62,6 +62,7 @@ namespace util
*/ */
inline void pressEnter() inline void pressEnter()
{ {
std::cout << std::endl;
system("pause"); system("pause");
} }
} }
@@ -21,4 +21,28 @@ namespace util
{ {
std::cout << "\x1B[2J\x1B[H" << std::flush; 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) + "...";
}
} }
@@ -107,3 +107,69 @@ bool util::isPasswordValid(const std::string& password)
return hasUpper && hasLower && hasDigit && hasSpecial; 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(
[&](const std::string&, User* user)
{
return (user->getUserName() == username && user->getState() == util::State::ACTIVE);
}
);
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(
[&](const std::string&, User* user)
{
return (user->getPhone() == phone && user->getState() == util::State::ACTIVE);
}
);
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(
[&](const std::string&, User* user)
{
return (user->getEmail() == email && user->getState() == util::State::ACTIVE);
}
);
return index != -1;
}
@@ -9,10 +9,15 @@
#include<string> #include<string>
#include<algorithm> #include<algorithm>
#include<cctype> #include<cctype>
#include "Map.h"
#include "User.h"
namespace util namespace util
{ {
bool isPhoneNumberValid(const std::string&); bool isPhoneNumberValid(const std::string&);
bool isEmailValid(const std::string&); bool isEmailValid(const std::string&);
bool isPasswordValid(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*>&);
} }
@@ -158,10 +158,33 @@ void AdminMenu::viewStockLevels()
{ {
util::clear(); util::clear();
auto inventoryItems = m_controller.getInventoryItems(); 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::cout << std::left << std::setw(15) << "Item ID"
<< std::setw(25) << "Part Name" << std::setw(25) << "Part Name"
<< std::setw(10) << "Quantity" << std::setw(15) << "Quantity"
<< std::setw(10) << "Price" << std::setw(15) << "Price"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < inventoryItems.getSize(); ++iterator) 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::cout << std::left << std::setw(15) << item->getId()
<< std::setw(25) << item->getPartName() << std::setw(25) << item->getPartName()
<< std::setw(10) << item->getQuantity() << std::setw(15) << item->getQuantity()
<< std::setw(10) << item->getPrice() << std::setw(15) << item->getPrice()
<< std::endl; << std::endl;
} }
} }
} }
std::cout << "\n";
util::pressEnter();
} }
/* /*
@@ -193,13 +218,15 @@ void AdminMenu::addInventoryItem()
int choice, quantity; int choice, quantity;
double price; double price;
std::string partName; 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); util::read(choice);
switch (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 : "; std::cout << "Part Name : ";
util::read(partName); util::read(partName);
std::cout << "Quantity : "; std::cout << "Quantity : ";
@@ -207,15 +234,21 @@ void AdminMenu::addInventoryItem()
std::cout << "Price : "; std::cout << "Price : ";
util::read(price); util::read(price);
m_controller.addInventoryItem(partName, quantity, 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; break;
} }
case 2: case 2:
{ {
util::clear();
std::cout << "Select Item to Restock\n";
auto inventoryItems = m_controller.getInventoryItems(); auto inventoryItems = m_controller.getInventoryItems();
addQuantityToItem(inventoryItems, m_controller); addQuantityToItem(inventoryItems, m_controller);
break; break;
} }
default:
{
std::cout << "\nEnter a valid choice.\n\n";
}
} }
util::pressEnter(); util::pressEnter();
} }
@@ -230,6 +263,7 @@ Return type: void
void AdminMenu::removeInventoryItem() void AdminMenu::removeInventoryItem()
{ {
util::clear(); util::clear();
std::cout << "Remove Inventory Item\n";
auto inventoryItems = m_controller.getInventoryItems(); auto inventoryItems = m_controller.getInventoryItems();
auto activeItems = filterActiveItems(inventoryItems); auto activeItems = filterActiveItems(inventoryItems);
int activeItemsSize = activeItems.getSize(); int activeItemsSize = activeItems.getSize();
@@ -273,8 +307,10 @@ void AdminMenu::checkStockAvailability()
{ {
util::clear(); util::clear();
std::string itemId; 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::read(itemId);
util::clear();
const InventoryItem* selectedItem = m_controller.getInventoryItem(itemId); const InventoryItem* selectedItem = m_controller.getInventoryItem(itemId);
if (selectedItem != nullptr) if (selectedItem != nullptr)
{ {
@@ -287,6 +323,10 @@ void AdminMenu::checkStockAvailability()
std::cout << "Quantity : " << selectedItem->getQuantity() << "\n"; std::cout << "Quantity : " << selectedItem->getQuantity() << "\n";
} }
} }
else
{
std::cout << "Item not Found" << std::endl;
}
util::pressEnter(); util::pressEnter();
} }
@@ -302,14 +342,16 @@ Returns:
void AdminMenu::assignJob() void AdminMenu::assignJob()
{ {
util::clear(); util::clear();
std::cout << "Assign Job to Technician\n";
std::string selectedService; std::string selectedService;
bool hasPendingService = false; bool hasPendingService = false;
auto currentBookings = m_controller.getServiceBookings(); auto currentBookings = m_controller.getServiceBookings();
auto pendingServiceBookings = filterActiveServiceBookings(currentBookings);
auto availableTechnicians = m_controller.getUsers(util::UserType::TECHNICIAN); 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 ServiceBooking*> serviceBookingsMap;
util::Map<int, const User*> currentAvailableTechniciansMap; util::Map<int, const User*> currentAvailableTechniciansMap;
if (listServiceBookings(currentBookings, bookingsSize, serviceBookingsMap)) if (listServiceBookings(pendingServiceBookings, bookingsSize, serviceBookingsMap))
{ {
const ServiceBooking* selectedService = selectPendingServiceBookings(serviceBookingsMap); const ServiceBooking* selectedService = selectPendingServiceBookings(serviceBookingsMap);
if (selectedService) if (selectedService)
@@ -325,14 +367,19 @@ void AdminMenu::assignJob()
{ {
m_controller.createJobCard(selectedService->getId(), selectedTechnician->getId(), servicesInBooking.getValueAt(iterator)->getId()); 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 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(); util::pressEnter();
} }
@@ -347,17 +394,24 @@ Returns:
void AdminMenu::createService() void AdminMenu::createService()
{ {
util::clear(); util::clear();
std::cout << "Create Service\n";
std::string serviceName; std::string serviceName;
double labourCost; double labourCost;
std::cout << "Enter the service name: "; std::cout << "Enter the service name: ";
util::read(serviceName); util::read(serviceName);
util::Map<std::string, const InventoryItem*> currentInventoryItems = m_controller.getInventoryItems(); util::Map<std::string, const InventoryItem*> currentInventoryItems = m_controller.getInventoryItems();
util::Map<std::string, const InventoryItem*> activeInventoryItems = filterActiveItems(currentInventoryItems);
util::Vector<std::string> selectedInventoryItems; util::Vector<std::string> selectedInventoryItems;
selectInventoryItems(currentInventoryItems,selectedInventoryItems); selectInventoryItems(activeInventoryItems,selectedInventoryItems);
std::cout << "Enter the labour cost: "; if (selectedInventoryItems.isEmpty())
{
util::pressEnter();
return;
}
std::cout << "\nEnter the labour cost: ";
util::read(labourCost); util::read(labourCost);
m_controller.createService(serviceName, selectedInventoryItems, labourCost); m_controller.createService(serviceName, selectedInventoryItems, labourCost);
std::cout << "Service created sucessfully.\n"; std::cout << "\nService created sucessfully.\n\n";
util::pressEnter(); util::pressEnter();
} }
@@ -372,17 +426,19 @@ Returns:
void AdminMenu::removeService() void AdminMenu::removeService()
{ {
util::clear(); util::clear();
std::cout << "Remove Service\n";
std::string selectedServiceID; std::string selectedServiceID;
util::Map<std::string, const Service*> currentServices = m_controller.getServices(); 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 != "") if (selectedServiceID != "")
{ {
m_controller.removeService(selectedServiceID); m_controller.removeService(selectedServiceID);
std::cout << "Service removed sucessfully."; std::cout << "Service removed successfully.\n\n";
} }
else else
{ {
std::cout << "Failed to remove service."; std::cout << "Failed to remove service.\n\n";
} }
util::pressEnter(); util::pressEnter();
} }
@@ -397,36 +453,37 @@ void AdminMenu::addTechnician()
{ {
util::clear(); util::clear();
std::string username, name, password, email, phoneNumber; 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); util::read(username);
std::cout << std::left << std::setw(25) << "Enter Technician Name: "; std::cout << "Enter Technician Name: ";
util::read(name); util::read(name);
std::cout << std::setw(25) << "Enter Technician Password: "; std::cout << "Enter Technician Password: ";
util::read(password); util::read(password);
if(!util::isPasswordValid(password)) if(!util::isPasswordValid(password))
{ {
std::cout << "Error: Password is invalid!"; std::cout << "\nError: Password is invalid!\n\n";
util::pressEnter(); util::pressEnter();
return; return;
} }
std::cout << std::setw(25) << "Enter Technician Email: "; std::cout << "Enter Technician Email: ";
util::read(email); util::read(email);
if(!util::isEmailValid(email)) if(!util::isEmailValid(email))
{ {
std::cout << "Error: Email is invalid!"; std::cout << "\nError: Email is invalid!\n\n";
util::pressEnter(); util::pressEnter();
return; return;
} }
std::cout << std::setw(25) << "Enter Technician Phone: "; std::cout << "Enter Technician Phone: ";
util::read(phoneNumber); util::read(phoneNumber);
if(!util::isPhoneNumberValid(phoneNumber)) if(!util::isPhoneNumberValid(phoneNumber))
{ {
std::cout << "Error: Phone Number is invalid!"; std::cout << "\nError: Phone Number is invalid!\n\n";
util::pressEnter(); util::pressEnter();
return; return;
} }
m_controller.createTechnician(username, name, password, email, phoneNumber); m_controller.createTechnician(username, name, password, email, phoneNumber);
std::cout << "\nTechnician Added Successfully.\n"; std::cout << "\nTechnician Added Successfully.\n\n";
util::pressEnter(); util::pressEnter();
} }
@@ -443,6 +500,7 @@ void AdminMenu::removeUser()
auto listOfUsers = m_controller.getUsers(); auto listOfUsers = m_controller.getUsers();
auto listOfActiveUsers = filterActiveUsers(listOfUsers); auto listOfActiveUsers = filterActiveUsers(listOfUsers);
int activeUserCount = listOfActiveUsers.getSize(); int activeUserCount = listOfActiveUsers.getSize();
std::cout << "Remove User \n";
if (activeUserCount < 1) if (activeUserCount < 1)
{ {
std::cout << "No Active users." << std::endl; std::cout << "No Active users." << std::endl;
@@ -477,7 +535,10 @@ Return type: void
void AdminMenu::createComboPackages() void AdminMenu::createComboPackages()
{ {
util::clear(); util::clear();
std::cout << "Create Combo Packages\n";
auto serviceList = m_controller.getServices(); auto serviceList = m_controller.getServices();
auto activeServices = filterActiveServices(serviceList);
int currentActiveServicesCount = activeServices.getSize();
const int NUMBER_OF_SERVICE_PER_PACKAGE = 2; const int NUMBER_OF_SERVICE_PER_PACKAGE = 2;
util::Vector<std::string> selectedServiceID; util::Vector<std::string> selectedServiceID;
for (int iterator = 0; iterator < NUMBER_OF_SERVICE_PER_PACKAGE; iterator++) for (int iterator = 0; iterator < NUMBER_OF_SERVICE_PER_PACKAGE; iterator++)
@@ -485,10 +546,10 @@ void AdminMenu::createComboPackages()
const Service* chosenService = nullptr; const Service* chosenService = nullptr;
while (true) while (true)
{ {
chosenService = selectServiceFromServices(serviceList); chosenService = selectServiceFromServices(activeServices);
if (chosenService == nullptr) if (!chosenService)
{ {
std::cout << "Failed to create combo package!"; std::cout << "Failed to create combo package!\n\n";
util::pressEnter(); util::pressEnter();
return; return;
} }
@@ -503,11 +564,19 @@ void AdminMenu::createComboPackages()
} }
if (alreadyChosen) if (alreadyChosen)
{ {
if (currentActiveServicesCount < 2)
{
break;
}
std::cout << "Service already selected. Please choose a different one." << std::endl; std::cout << "Service already selected. Please choose a different one." << std::endl;
continue; continue;
} }
selectedServiceID.push_back(chosenService->getId()); selectedServiceID.push_back(chosenService->getId());
util::clear(); break;
}
if (currentActiveServicesCount < 2)
{
std::cout << "All the available services selected\n\n";
break; break;
} }
} }
@@ -537,16 +606,17 @@ Return type: void
void AdminMenu::removeComboPackage() void AdminMenu::removeComboPackage()
{ {
util::clear(); util::clear();
std::cout << "Remove Combo Package\n";
util::Map<std::string, const ComboPackage*> currentComboPackages = m_controller.getComboPackages(); util::Map<std::string, const ComboPackage*> currentComboPackages = m_controller.getComboPackages();
std::string selectedComboPackageID = selectComboPackage(currentComboPackages); std::string selectedComboPackageID = selectComboPackage(currentComboPackages);
if (selectedComboPackageID != "") if (!selectedComboPackageID.empty())
{ {
m_controller.removeComboPackage(selectedComboPackageID); m_controller.removeComboPackage(selectedComboPackageID);
std::cout << "Combo Package removed successfully.\n"; std::cout << "Combo Package removed successfully.\n\n";
} }
else else
{ {
std::cout << "Combo package removal failed.\n"; std::cout << "Combo package removal failed.\n\n";
} }
util::pressEnter(); util::pressEnter();
} }
@@ -144,11 +144,12 @@ void CustomerMenu::updateDetails()
{ {
std::string email, phone; std::string email, phone;
util::clear(); util::clear();
std::cout << "Update Details\n";
std::cout << "Enter new email: "; std::cout << "Enter new email: ";
util::read(email); util::read(email);
if (!util::isEmailValid(email)) if (!util::isEmailValid(email))
{ {
std::cout << "Error: Email is invalid!"; std::cout << "Error: Email is invalid!\n";
util::pressEnter(); util::pressEnter();
return; return;
} }
@@ -156,12 +157,12 @@ void CustomerMenu::updateDetails()
util::read(phone); util::read(phone);
if (!util::isPhoneNumberValid(phone)) if (!util::isPhoneNumberValid(phone))
{ {
std::cout << "Error: Phone number is invalid!"; std::cout << "Error: Phone number is invalid!\n";
util::pressEnter(); util::pressEnter();
return; return;
} }
m_controller.updateUserDetails(email, phone); m_controller.updateUserDetails(email, phone);
std::cout << "Profile details updated successfully"; std::cout << "Profile details updated successfully\n";
util::pressEnter(); util::pressEnter();
} }
@@ -175,9 +176,16 @@ Return type: void
void CustomerMenu::selectService() void CustomerMenu::selectService()
{ {
std::string vehicleNumber, vehicleBrand, vehicleModel; std::string vehicleNumber, vehicleBrand, vehicleModel;
auto services = m_controller.getServices();
util::Vector<std::string> selectedServices;
util::clear(); 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); const Service* selectedService = selectServiceFromServices(services);
if (selectedService == nullptr) if (selectedService == nullptr)
{ {
@@ -187,6 +195,7 @@ void CustomerMenu::selectService()
} }
selectedServices.push_back(selectedService->getId()); selectedServices.push_back(selectedService->getId());
util::clear(); util::clear();
std::cout << "Enter Vehicle Details\n";
std::cout << "Enter vehicle number: "; std::cout << "Enter vehicle number: ";
util::read(vehicleNumber); util::read(vehicleNumber);
std::cout << "Enter vehicle brand: "; std::cout << "Enter vehicle brand: ";
@@ -208,16 +217,27 @@ Return type: void
void CustomerMenu::selectComboPackage() void CustomerMenu::selectComboPackage()
{ {
std::string vehicleNumber, vehicleBrand, vehicleModel; std::string vehicleNumber, vehicleBrand, vehicleModel;
auto comboPackages = m_controller.getComboPackages();
util::clear(); util::clear();
const ComboPackage* selectedComboPackage = selectComboPackageFromPackages(comboPackages); std::cout << "Select a Combo Package\n";
if (selectedComboPackage == nullptr) auto comboPackages = m_controller.getComboPackages();
util::Map<std::string, const ComboPackage*> activeComboPackages = filterComboPackages(comboPackages);
if (activeComboPackages.isEmpty())
{ {
std::cout << "Failed to book combo package!"; std::cout << "No combo packages available!\n\n";
util::pressEnter(); util::pressEnter();
return; return;
} }
const ComboPackage* selectedComboPackage = selectComboPackageFromPackages(activeComboPackages);
if (selectedComboPackage == nullptr)
{
std::cout << "Failed to book combo package!\n\n";
util::pressEnter();
return;
}
std::cout << "Combo Package selected\n";
util::pressEnter();
util::clear(); util::clear();
std::cout << "Enter the vehicle details\n";
std::cout << "Enter vehicle number: "; std::cout << "Enter vehicle number: ";
util::read(vehicleNumber); util::read(vehicleNumber);
std::cout << "Enter vehicle brand: "; std::cout << "Enter vehicle brand: ";
@@ -225,7 +245,7 @@ void CustomerMenu::selectComboPackage()
std::cout << "Enter vehicle model: "; std::cout << "Enter vehicle model: ";
util::read(vehicleModel); util::read(vehicleModel);
m_controller.purchaseComboPackage(selectedComboPackage->getId(), vehicleNumber, vehicleBrand, vehicleModel); m_controller.purchaseComboPackage(selectedComboPackage->getId(), vehicleNumber, vehicleBrand, vehicleModel);
std::cout << "Combo Package has been booked successfully"; std::cout << "Combo Package has been booked successfully\n\n";
util::pressEnter(); util::pressEnter();
} }
@@ -245,16 +265,17 @@ void CustomerMenu::viewServiceHistory()
const User* currentUser = m_controller.getAuthenticatedUser(); const User* currentUser = m_controller.getAuthenticatedUser();
std::string currentUserID = currentUser->getId(); std::string currentUserID = currentUser->getId();
util::Map<std::string, const ServiceBooking*> serviceBookingsByCurrentUser = m_controller.getServiceBookingsByUser(currentUserID); util::Map<std::string, const ServiceBooking*> serviceBookingsByCurrentUser = m_controller.getServiceBookingsByUser(currentUserID);
std::cout << "View Service History" << std::endl;
if (serviceBookingsByCurrentUser.getSize() != 0) if (serviceBookingsByCurrentUser.getSize() != 0)
{ {
std::cout << std::left std::cout << std::left
<< std::setw(12) << "Booking ID" << std::setw(15) << "Booking ID"
<< std::setw(20) << "Technician" << std::setw(20) << "Technician"
<< std::setw(15) << "Vehicle Brand" << std::setw(20) << "Vehicle Brand"
<< std::setw(15) << "Vehicle Number" << std::setw(20) << "Vehicle Number"
<< std::setw(15) << "Vehicle Model" << std::setw(20) << "Vehicle Model"
<< std::setw(10) << "Discount %" << std::setw(20) << "Discount %"
<< std::setw(12) << "Status" << std::setw(20) << "Status"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < serviceBookingsByCurrentUser.getSize(); iterator++) for (int iterator = 0; iterator < serviceBookingsByCurrentUser.getSize(); iterator++)
{ {
@@ -263,13 +284,13 @@ void CustomerMenu::viewServiceHistory()
? "Not Assigned" ? "Not Assigned"
: currentBooking->getAssignedTechnician()->getName(); : currentBooking->getAssignedTechnician()->getName();
std::cout << std::left std::cout << std::left
<< std::setw(12) << currentBooking->getId() << std::setw(15) << currentBooking->getId()
<< std::setw(20) << technicianName << std::setw(20) << technicianName
<< std::setw(15) << currentBooking->getVehicleBrand() << std::setw(20) << currentBooking->getVehicleBrand()
<< std::setw(15) << currentBooking->getVehicleNumber() << std::setw(20) << currentBooking->getVehicleNumber()
<< std::setw(15) << currentBooking->getVehicleModel() << std::setw(20) << currentBooking->getVehicleModel()
<< std::setw(10) << currentBooking->getDiscountPercentage() << std::setw(20) << currentBooking->getDiscountPercentage()
<< std::setw(12) << util::getServiceJobStatusString(currentBooking->getStatus()) << std::setw(20) << util::getServiceJobStatusString(currentBooking->getStatus())
<< std::endl; << std::endl;
hasServiceHistory = true; hasServiceHistory = true;
} }
@@ -293,11 +314,35 @@ Returns:
void CustomerMenu::completePayments() void CustomerMenu::completePayments()
{ {
util::clear(); util::clear();
std::cout << "Complete Payments\n";
util::Map<std::string, const Invoice*> currentInvoices = m_controller.getInvoicesByUser(); 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); std::string selectedID = selectInvoiceFromUserForPayment(currentInvoices);
if (selectedID == "") if (selectedID == "")
{ {
std::cout << "Payment failed.\n"; std::cout << "Payment failed.\n";
util::pressEnter();
return; return;
} }
util::PaymentMode paymentMode = selectPaymentMode(); util::PaymentMode paymentMode = selectPaymentMode();
@@ -317,6 +362,7 @@ Returns:
void CustomerMenu::viewInvoices() void CustomerMenu::viewInvoices()
{ {
util::clear(); util::clear();
std::cout << "View Invoices\n";
util::Map<std::string, const Invoice*> currentUserInvoices = m_controller.getInvoicesByUser(); util::Map<std::string, const Invoice*> currentUserInvoices = m_controller.getInvoicesByUser();
displayInvoices(currentUserInvoices); displayInvoices(currentUserInvoices);
util::pressEnter(); util::pressEnter();
@@ -39,36 +39,34 @@ Returns:
*/ */
inline std::string selectServicesToRemove(util::Map<std::string, const Service*> currentServices) 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; util::Map<int, const Service*> currentServicesMap;
bool hasServices = false;
int currentIndex = 1, choice; int currentIndex = 1, choice;
std::cout << std::left std::cout << std::left
<< std::setw(6) << "Index" << std::setw(6) << "Index"
<< std::setw(12) << "Service ID" << std::setw(12) << "Service ID"
<< std::setw(20) << "Name" << std::setw(35) << "Name"
<< std::setw(10) << "Labor Cost" << std::setw(10) << "Labor Cost"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < currentServices.getSize(); iterator++) for (int iterator = 0; iterator < currentServices.getSize(); iterator++)
{ {
const Service* currentService = currentServices.getValueAt(iterator); const Service* currentService = currentServices.getValueAt(iterator);
if (currentService->getState() == util::State::INACTIVE) if (currentService == nullptr || currentService->getState() == util::State::INACTIVE)
{ {
continue; continue;
} }
std::cout << std::left std::cout << std::left
<< std::setw(6) << currentIndex << std::setw(6) << currentIndex
<< std::setw(12) << currentService->getId() << std::setw(12) << currentService->getId()
<< std::setw(20) << currentService->getName() << std::setw(35) << util::truncateString(currentService->getName(), 30)
<< std::setw(10) << currentService->getLaborCost() << std::setw(10) << currentService->getLaborCost()
<< std::endl; << std::endl;
hasServices = true;
currentServicesMap.insert(currentIndex++, currentService); currentServicesMap.insert(currentIndex++, currentService);
} }
if (!hasServices)
{
std::cout << "No services currently available." << std::endl;
return "";
}
std::cout << "Enter your choice: "; std::cout << "Enter your choice: ";
util::read(choice); util::read(choice);
if (currentServicesMap.find(choice) != -1) if (currentServicesMap.find(choice) != -1)
@@ -77,7 +75,7 @@ inline std::string selectServicesToRemove(util::Map<std::string, const Service*>
} }
else else
{ {
std::cout << "Invalid choice." << std::endl; std::cout << "Invalid index." << std::endl;
return ""; return "";
} }
} }
@@ -91,18 +89,21 @@ Parameters:
Returns: Returns:
- void - 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; bool doRun = true;
util::Map<int, const InventoryItem*> currentInventoryMap; util::Map<int, const InventoryItem*> currentInventoryMap;
int currentIndex = 1;
int choice; int choice;
if (currentInventoryItems.getSize() == 0) if (currentInventoryItems.isEmpty())
{ {
std::cout << "Inventory empty."; std::cout << "No Items Present, Inventory empty.\n";
return;
} }
while (doRun) while (doRun)
{ {
util::clear();
std::cout << "Create Service\n";
std::cout << "\nSelect Required Items\n";
bool hasInventoryItems = false; bool hasInventoryItems = false;
int currentIndex = 1; int currentIndex = 1;
currentInventoryMap.clear(); currentInventoryMap.clear();
@@ -116,7 +117,20 @@ static void selectInventoryItems(util::Map<std::string, const InventoryItem*>& c
for (int iterator = 0; iterator < currentInventoryItems.getSize(); iterator++) for (int iterator = 0; iterator < currentInventoryItems.getSize(); iterator++)
{ {
const InventoryItem* currentInventoryItem = currentInventoryItems.getValueAt(iterator); const InventoryItem* currentInventoryItem = currentInventoryItems.getValueAt(iterator);
if (currentInventoryItem->getState() == util::State::INACTIVE) if (currentInventoryItem == nullptr || currentInventoryItem->getState() == util::State::INACTIVE)
{
continue;
}
bool alreadySelected = false;
for (int iteratorOne = 0; iteratorOne < selectedInventoryItems.getSize(); iteratorOne++)
{
if (selectedInventoryItems[iteratorOne] == currentInventoryItem->getId())
{
alreadySelected = true;
break;
}
}
if (alreadySelected)
{ {
continue; continue;
} }
@@ -127,35 +141,54 @@ static void selectInventoryItems(util::Map<std::string, const InventoryItem*>& c
<< std::setw(10) << currentInventoryItem->getPrice() << std::setw(10) << currentInventoryItem->getPrice()
<< std::setw(10) << currentInventoryItem->getQuantity() << std::setw(10) << currentInventoryItem->getQuantity()
<< std::endl; << std::endl;
hasInventoryItems = true;
currentInventoryMap.insert(currentIndex++, currentInventoryItem); currentInventoryMap.insert(currentIndex++, currentInventoryItem);
hasInventoryItems = true;
} }
if (!hasInventoryItems) if (!hasInventoryItems)
{ {
std::cout << "No items present in the inventory." << std::endl;
doRun = false;
break; break;
} }
std::cout << "Select the item (Index) or enter -1 to exit: "; std::cout << "Select the item (Index) or enter 0 to exit: ";
util::read(choice); util::read(choice);
if (choice == 0)
if (choice == -1)
{ {
doRun = false; doRun = false;
} }
else if (currentInventoryMap.find(choice) != -1) else if (currentInventoryMap.find(choice) != -1)
{ {
selectedInventoryItems.push_back(currentInventoryMap.getValueAt(choice)->getId()); selectedInventoryItems.push_back(currentInventoryMap.getValueAt(currentInventoryMap.find(choice))->getId());
std::cout << "Item added successfully." << std::endl; std::cout << "Item added successfully.\n" << std::endl;
util::pressEnter();
} }
else 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 Function: listServiceBookings
Description: Lists all pending service bookings and maps them to indices for selection. Description: Lists all pending service bookings and maps them to indices for selection.
@@ -166,10 +199,14 @@ Parameters:
Returns: Returns:
- bool: True if pending services exist, False otherwise - 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)
{ {
if (currentBookings.getSize() == 0)
{
return false;
}
int currentIndex = 1; int currentIndex = 1;
bool hasPendingService = false; std::cout << "\nSelect Service Booking" << std::endl;
std::cout << std::left std::cout << std::left
<< std::setw(10) << "Index" << std::setw(10) << "Index"
<< std::setw(10) << "ID" << std::setw(10) << "ID"
@@ -179,15 +216,12 @@ static bool listServiceBookings(util::Map<std::string, const ServiceBooking*>& c
<< std::setw(15) << "VehicleNo" << std::setw(15) << "VehicleNo"
<< std::setw(15) << "Brand" << std::setw(15) << "Brand"
<< std::setw(15) << "Model" << std::setw(15) << "Model"
<< std::setw(20) << "Technician"
<< std::setw(15) << "TechnicianID"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < bookingsSize; iterator++) for (int iterator = 0; iterator < bookingsSize; iterator++)
{ {
const ServiceBooking* currentBooking = currentBookings.getValueAt(iterator); const ServiceBooking* currentBooking = currentBookings.getValueAt(iterator);
if (currentBooking && currentBooking->getStatus() == util::ServiceJobStatus::PENDING) if (currentBooking && currentBooking->getStatus() == util::ServiceJobStatus::PENDING)
{ {
hasPendingService = true;
const User* currentAssignedTechnician = currentBooking->getAssignedTechnician(); const User* currentAssignedTechnician = currentBooking->getAssignedTechnician();
std::cout << std::left std::cout << std::left
<< std::setw(10) << currentIndex << std::setw(10) << currentIndex
@@ -198,16 +232,9 @@ static bool listServiceBookings(util::Map<std::string, const ServiceBooking*>& c
<< std::setw(15) << currentBooking->getVehicleNumber() << std::setw(15) << currentBooking->getVehicleNumber()
<< std::setw(15) << currentBooking->getVehicleBrand() << std::setw(15) << currentBooking->getVehicleBrand()
<< std::setw(15) << currentBooking->getVehicleModel() << 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; << std::endl;
serviceBookingsMap.insert(currentIndex++, currentBooking); serviceBookingsMap.insert(currentIndex++, currentBooking);
} }
}
if (!hasPendingService)
{
std::cout << "No pending service available." << std::endl;
return false;
} }
return true; return true;
} }
@@ -220,18 +247,18 @@ Parameters:
Returns: Returns:
- const ServiceBooking*: Pointer to the selected booking, or nullptr if invalid - 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; int userInputIndex;
std::cout << "Enter a valid service index: "; std::cout << "\nEnter a service index: ";
util::read(userInputIndex); util::read(userInputIndex);
if (serviceBookingsMap.find(userInputIndex) != -1) if (serviceBookingsMap.find(userInputIndex) != -1)
{ {
return serviceBookingsMap.getValueAt(userInputIndex); return serviceBookingsMap.getValueAt(serviceBookingsMap.find(userInputIndex));
} }
else else
{ {
std::cout << "Enter a valid index."; std::cout << "Enter a valid index.\n\n";
return nullptr; return nullptr;
} }
} }
@@ -246,10 +273,11 @@ Parameters:
Returns: Returns:
- void - 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; bool hasTechnicians = false;
int currentIndex = 1; int currentIndex = 1;
std::cout << "\nSelect Technician\n";
std::cout << std::left std::cout << std::left
<< std::setw(6) << "Index" << std::setw(6) << "Index"
<< std::setw(15) << "Technician ID" << std::setw(15) << "Technician ID"
@@ -272,7 +300,7 @@ static void listAvailableTechnicians(util::Map<std::string, const User*> current
} }
if (!hasTechnicians) if (!hasTechnicians)
{ {
std::cout << "No technicians currently available."; std::cout << "No technicians currently available.\n\n";
} }
} }
@@ -284,17 +312,18 @@ Parameters:
Returns: Returns:
- const User*: Pointer to the selected technician, or nullptr if invalid - 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; int userInputIndex;
std::cout << "\nEnter technician index: ";
util::read(userInputIndex); util::read(userInputIndex);
if (currentAvailableTechniciansMap.find(userInputIndex) != -1) if (currentAvailableTechniciansMap.find(userInputIndex) != -1)
{ {
return currentAvailableTechniciansMap.getValueAt(userInputIndex); return currentAvailableTechniciansMap.getValueAt(currentAvailableTechniciansMap.find(userInputIndex));
} }
else else
{ {
std::cout << "Enter a valid index."; std::cout << "Enter a valid index.\n\n";
return nullptr; return nullptr;
} }
} }
@@ -307,20 +336,20 @@ Parameters:
Returns: Returns:
- std::string: ID of the selected invoice, or empty string if none selected - 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; int currentIndex = 1, choice;
util::Map<int, const Invoice*> pendingInvoicesForPayment; util::Map<int, const Invoice*> pendingInvoicesForPayment;
std::cout << std::left std::cout << std::left
<< std::setw(6) << "Index" << std::setw(8) << "Index"
<< std::setw(12) << "BookingID" << std::setw(15) << "Booking ID"
<< std::setw(15) << "VehicleBrand" << std::setw(20) << "Vehicle Brand"
<< std::setw(15) << "VehicleNumber" << std::setw(20) << "Vehicle Number"
<< std::setw(12) << "TechID" << std::setw(18) << "Technician ID"
<< std::setw(20) << "TechnicianName" << std::setw(25) << "Technician Name"
<< std::setw(10) << "Discount(%)" << std::setw(15) << "Discount(%)"
<< std::setw(12) << "TotalAmount" << std::setw(15) << "TotalAmount"
<< std::setw(20) << "InvoiceDate" << std::setw(22) << "Invoice Timestamp"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++) for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++)
{ {
@@ -329,17 +358,17 @@ static std::string selectInvoiceFromUserForPayment(const util::Map<std::string,
{ {
const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician(); const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician();
std::cout << std::left std::cout << std::left
<< std::setw(6) << currentIndex << std::setw(8) << currentIndex
<< std::setw(12) << currentInvoice->getBookingId() << std::setw(15) << currentInvoice->getBookingId()
<< std::setw(15) << currentInvoice->getBooking()->getVehicleBrand() << std::setw(20) << currentInvoice->getBooking()->getVehicleBrand()
<< std::setw(15) << currentInvoice->getBooking()->getVehicleNumber() << std::setw(20) << currentInvoice->getBooking()->getVehicleNumber()
<< std::setw(12) << ((currentTechnician != nullptr && currentTechnician->getId() != "") ? << std::setw(18) << ((currentTechnician != nullptr && currentTechnician->getId() != "") ?
currentTechnician->getId() : "Null") currentTechnician->getId() : "Null")
<< std::setw(20) << ((currentTechnician != nullptr && currentTechnician->getName() != "") ? << std::setw(25) << ((currentTechnician != nullptr && currentTechnician->getName() != "") ?
currentTechnician->getName() : "Null") currentTechnician->getName() : "Null")
<< std::setw(10) << currentInvoice->getDiscountPercentage() << std::setw(15) << currentInvoice->getDiscountPercentage()
<< std::setw(12) << currentInvoice->getTotalAmount() << std::setw(15) << currentInvoice->getTotalAmount()
<< std::setw(20) << currentInvoice->getInvoiceDate().toString() << std::setw(22) << currentInvoice->getInvoiceDate().toString()
<< std::endl; << std::endl;
pendingInvoicesForPayment.insert(currentIndex++, currentInvoice); pendingInvoicesForPayment.insert(currentIndex++, currentInvoice);
} }
@@ -359,7 +388,7 @@ static std::string selectInvoiceFromUserForPayment(const util::Map<std::string,
} }
else else
{ {
std::cout << "Invalid choice.\n"; std::cout << "Invalid index.\n";
return ""; return "";
} }
} }
@@ -372,9 +401,12 @@ Parameters:
Returns: Returns:
- util::PaymentMode: Selected payment mode - util::PaymentMode: Selected payment mode
*/ */
static util::PaymentMode selectPaymentMode() inline util::PaymentMode selectPaymentMode()
{ {
int choice; int choice;
while (true)
{
util::clear();
std::cout << "Enter the payment Mode\n1.OFFLINE\n2.ONLINE\nChoice: "; std::cout << "Enter the payment Mode\n1.OFFLINE\n2.ONLINE\nChoice: ";
util::read(choice); util::read(choice);
if (choice == 1) if (choice == 1)
@@ -389,8 +421,76 @@ static util::PaymentMode selectPaymentMode()
} }
else else
{ {
std::cout << "Invalid choice, Offline mode selected.\n"; std::cout << "Invalid choice. Try again.\n";
return util::PaymentMode::OFFLINE; util::pressEnter();
}
}
}
/*
Function: displayInvoicesInTabularForm
Description:
Displays all invoices in a tabular format. Each row shows booking details,
vehicle info, technician details, discount, total amount, invoice date,
and payment status. If inventory items exist for an invoice, they are
displayed in a separate table below the invoice row.
Parameters:
- currentInvoices: util::Map<std::string, const Invoice*>
Map of invoice IDs to Invoice pointers.
Returns:
- void
*/
inline const Invoice* selectInvoiceToDisplay(util::Map<std::string, const Invoice*>& currentInvoices)
{
int currentIndex = 1, choice;
util::Map<int, const Invoice*> currentInvoicesIndexMap;
if (currentInvoices.isEmpty())
{
std::cout << "No invoices available.\n\n";
return nullptr;
}
std::cout
<< std::left
<< std::setw(10) << "Index"
<< std::setw(12) << "BookingID"
<< std::setw(20) << "Vehicle Number"
<< std::setw(20) << "Technician Name"
<< std::setw(15) << "Total Amount"
<< std::setw(25) << "Invoice Date"
<< std::setw(20) << "Payment Status"
<< std::setw(15) << "Payment Mode"
<< std::endl;
for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++)
{
const Invoice* currentInvoice = currentInvoices.getValueAt(iterator);
if (!currentInvoice)
{
continue;
}
const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician();
std::cout << std::left
<< std::setw(10) << currentIndex
<< std::setw(12) << currentInvoice->getBookingId()
<< std::setw(20) << currentInvoice->getBooking()->getVehicleNumber()
<< std::setw(20) << ((currentTechnician && !currentTechnician->getName().empty()) ? currentTechnician->getName() : "NULL")
<< std::setw(15) << currentInvoice->getTotalAmount()
<< std::setw(25) << currentInvoice->getInvoiceDate().toString()
<< std::setw(20) << util::getPaymentStatusString(currentInvoice->getStatus())
<< std::setw(15) << util::getPaymentModeString(currentInvoice->getPaymentMethod())
<< std::endl;
currentInvoicesIndexMap.insert(currentIndex++, currentInvoice);
}
std::cout << "Enter an index: ";
util::read(choice);
int currentSelectedIndex = currentInvoicesIndexMap.find(choice);
if (currentSelectedIndex != -1)
{
return currentInvoicesIndexMap.getValueAt(currentSelectedIndex);
}
else
{
std::cout << "Enter a valid index.\n";
return nullptr;
} }
} }
@@ -405,37 +505,55 @@ Returns:
Throws: Throws:
- std::runtime_error if a null invoice is encountered - 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)
{ {
std::cout << std::endl;
if (currentUserInvoices.getSize() == 0) if (currentUserInvoices.getSize() == 0)
{ {
std::cout << "No invoices found for this account." << std::endl; std::cout << "No invoices found for this account.\n\n";
util::pressEnter();
return; return;
} }
else else
{ {
for (int index = 0; index < currentUserInvoices.getSize(); index++) bool doRun = true;
do
{ {
const Invoice* currentInvoice = currentUserInvoices.getValueAt(index); const Invoice* selectedInvoice;
if (currentInvoice) int choice;
selectedInvoice = selectInvoiceToDisplay(currentUserInvoices);
if (selectedInvoice)
{ {
const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician(); const User* currentTechnician = selectedInvoice->getBooking()->getAssignedTechnician();
std::cout << "\nInvoice Details\n"; util::clear();
std::cout << "Booking ID: " << currentInvoice->getBookingId() << std::endl; std::cout << "Invoice Details\n";
std::cout << "Vehicle Brand: " << currentInvoice->getBooking()->getVehicleBrand() << std::endl; std::cout << std::left << std::setw(20) << "Booking ID:"
std::cout << "Vehicle Number: " << currentInvoice->getBooking()->getVehicleNumber() << std::endl; << selectedInvoice->getBookingId() << std::endl;
std::cout << "Technician ID: " << std::cout << std::left << std::setw(20) << "Vehicle Brand:"
((currentTechnician != nullptr && currentTechnician->getId() != "") ? << selectedInvoice->getBooking()->getVehicleBrand() << std::endl;
currentTechnician->getId() : "Null") << std::endl; std::cout << std::left << std::setw(20) << "Vehicle Number:"
std::cout << "Technician Name: " << << selectedInvoice->getBooking()->getVehicleNumber() << std::endl;
((currentTechnician != nullptr && currentTechnician->getName() != "") ? std::cout << std::left << std::setw(20) << "Technician ID:"
currentTechnician->getName() : "Null") << std::endl; << ((currentTechnician != nullptr && !currentTechnician->getId().empty())
std::cout << "Discount(%): " << currentInvoice->getDiscountPercentage() << std::endl; ? currentTechnician->getId() : "NULL") << std::endl;
std::cout << "Total Amount: " << currentInvoice->getTotalAmount() << std::endl; std::cout << std::left << std::setw(20) << "Technician Name:"
std::cout << "Invoice Date: " << currentInvoice->getInvoiceDate().toString() << std::endl; << ((currentTechnician != nullptr && !currentTechnician->getName().empty())
std::cout << "Payment Status: " << util::getPaymentStatusString(currentInvoice->getStatus()) << std::endl; ? currentTechnician->getName() : "NULL") << std::endl;
auto inventoryItemsInInvoice = currentInvoice->getParts(); std::cout << std::left << std::setw(20) << "Discount(%):"
<< selectedInvoice->getDiscountPercentage() << std::endl;
std::cout << std::left << std::setw(20) << "Total Amount:"
<< selectedInvoice->getTotalAmount() << std::endl;
std::cout << std::left << std::setw(20) << "Invoice Date:"
<< selectedInvoice->getInvoiceDate().toString() << std::endl;
std::cout << std::left << std::setw(20) << "Payment Status:"
<< util::getPaymentStatusString(selectedInvoice->getStatus()) << std::endl;
std::cout << std::left << std::setw(20) << "Payment Mode:"
<< util::getPaymentModeString(selectedInvoice->getPaymentMethod()) << std::endl;
auto inventoryItemsInInvoice = selectedInvoice->getParts();
if (inventoryItemsInInvoice.isEmpty())
{
std::cout << "No inventory items used.\n\n";
continue;
}
std::cout << "\nItems Used:\n"; std::cout << "\nItems Used:\n";
std::cout << std::left std::cout << std::left
<< std::setw(20) << "ItemName" << std::setw(20) << "ItemName"
@@ -452,14 +570,53 @@ static void displayInvoices(util::Map<std::string, const Invoice*> currentUserIn
<< std::setw(10) << currentItem->getPrice() << std::setw(10) << currentItem->getPrice()
<< std::endl; << std::endl;
} }
std::cout << "\n\nDo you want to display another Invoice (1-Yes, 2-No): ";
util::read(choice);
if (choice == 1)
{
doRun = true;
util::clear();
}
else if (choice == 2)
{
doRun = false;
} }
else else
{ {
throw std::runtime_error("Null invoice encountered while displaying invoices."); std::cout << "Invalid choice\n";
util::pressEnter(); doRun = false;
} }
} }
else
{
std::cout << "Unable to fetch the selected invoice\n";
doRun = false;
} }
} while (doRun);
}
}
/*
Function: filterStartedJobCards
Description: Filters the given list of job cards and returns only those with status STARTED.
Parameters:
- assignedJobCards: Map of job card IDs to JobCard pointers.
Returns:
- util::Map<std::string, const JobCard*> containing only job cards with status STARTED.
*/
inline util::Map<std::string, const JobCard*> filterStartedJobCards(util::Map<std::string, const JobCard*>& assignedJobCards)
{
util::Map<std::string, const JobCard*> startedJobCards;
for (int iterator = 0; iterator < assignedJobCards.getSize(); iterator++)
{
const JobCard* currentJobCard = assignedJobCards.getValueAt(iterator);
if (currentJobCard && currentJobCard->getStatus() == util::ServiceJobStatus::STARTED)
{
startedJobCards.insert(currentJobCard->getId(), currentJobCard);
}
}
return startedJobCards;
} }
/* /*
@@ -471,11 +628,17 @@ Parameters:
Returns: Returns:
- std::string: ID of the selected job card, or empty string if none selected - 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;
if (assignedJobCards.getSize() == 0)
{
std::cout << "No started jobs available to complete.\n";
return "";
}
int currentIndex = 1; int currentIndex = 1;
int choice; int choice;
bool hasIncompleteJobCard = false; std::cout << std::endl;
std::cout << std::left std::cout << std::left
<< std::setw(6) << "Index" << std::setw(6) << "Index"
<< std::setw(12) << "BookingID" << std::setw(12) << "BookingID"
@@ -494,14 +657,8 @@ static std::string selectJobCardToComplete(util::Map<std::string, const JobCard*
<< std::setw(20) << currentJobCard->getService()->getName() << std::setw(20) << currentJobCard->getService()->getName()
<< std::setw(12) << currentJobCard->getServiceId() << std::setw(12) << currentJobCard->getServiceId()
<< std::endl; << std::endl;
hasIncompleteJobCard = true;
incompleteJobCards.insert(currentIndex++, currentJobCard); incompleteJobCards.insert(currentIndex++, currentJobCard);
} }
}
if (!hasIncompleteJobCard)
{
std::cout << "No pending jobs are present.\n";
return "";
} }
std::cout << "Select the Job Card to complete (Index): "; std::cout << "Select the Job Card to complete (Index): ";
util::read(choice); util::read(choice);
@@ -513,7 +670,8 @@ static std::string selectJobCardToComplete(util::Map<std::string, const JobCard*
} }
else else
{ {
std::cout << "Invalid choice.\n"; std::cout << "Invalid index.\n";
std::cout << "Failed to complete jobs.\n\n";
return ""; return "";
} }
} }
@@ -528,16 +686,11 @@ Return type: const Notification* - pointer to the selected notification
*/ */
inline const Notification* selectNotification(const util::Vector<const Notification*>& notifications) 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; util::Map<int, const Notification*> indexedNotifications;
std::cout << std::left std::cout << std::left
<< std::setw(6) << "Index" << std::setw(10) << "Index"
<< std::setw(15) << "ID" << std::setw(15) << "ID"
<< std::setw(30) << "Title" << std::setw(35) << "Title"
<< std::setw(25) << "Timestamp" << std::setw(25) << "Timestamp"
<< std::endl; << std::endl;
int currentIndex = 1; int currentIndex = 1;
@@ -547,9 +700,9 @@ inline const Notification* selectNotification(const util::Vector<const Notificat
if (currentNotification) if (currentNotification)
{ {
std::cout << std::left std::cout << std::left
<< std::setw(6) << currentIndex << std::setw(10) << currentIndex
<< std::setw(15) << currentNotification->getId() << 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::setw(25) << currentNotification->getCreatedAt().toString()
<< std::endl; << std::endl;
indexedNotifications.insert(currentIndex, currentNotification); indexedNotifications.insert(currentIndex, currentNotification);
@@ -561,7 +714,7 @@ inline const Notification* selectNotification(const util::Vector<const Notificat
util::read(selectedIndex); util::read(selectedIndex);
if (!indexedNotifications.containsKey(selectedIndex)) if (!indexedNotifications.containsKey(selectedIndex))
{ {
std::cout << "Invalid selection." << std::endl; std::cout << "Invalid index." << std::endl;
return nullptr; return nullptr;
} }
return indexedNotifications[selectedIndex]; return indexedNotifications[selectedIndex];
@@ -602,6 +755,13 @@ inline void viewAndDeleteNotification(Controller& controller)
{ {
util::clear(); util::clear();
auto notifications = controller.getNotifications(); 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); const Notification* selectedNotification = selectNotification(notifications);
if (!selectedNotification) if (!selectedNotification)
{ {
@@ -623,10 +783,16 @@ Return type: void
inline void changePasswordHelper(Controller& controller) inline void changePasswordHelper(Controller& controller)
{ {
util::clear(); 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) while (true)
{ {
util::clear(); util::clear();
std::cout << "Change Password\n";
std::cout << "Enter new password: "; std::cout << "Enter new password: ";
util::read(newPassword); util::read(newPassword);
if (!util::isPasswordValid(newPassword)) if (!util::isPasswordValid(newPassword))
@@ -635,6 +801,20 @@ inline void changePasswordHelper(Controller& controller)
util::pressEnter(); util::pressEnter();
continue; 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); controller.changePassword(newPassword);
std::cout << "Password changed successfully\n"; std::cout << "Password changed successfully\n";
util::pressEnter(); util::pressEnter();
@@ -655,7 +835,7 @@ inline util::Map<std::string, const User*> filterActiveUsers(const util::Map<std
for (int index = 0; index < inventorySize; index++) for (int index = 0; index < inventorySize; index++)
{ {
const User* user = listOfUsers.getValueAt(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); activeUsers.insert(user->getId(), user);
} }
@@ -675,6 +855,7 @@ inline void displayAllActiveUsers(util::Map<std::string, const User*>& activeUse
std::cout << std::left << std::setw(10) << "Index" std::cout << std::left << std::setw(10) << "Index"
<< std::setw(15) << "User ID" << std::setw(15) << "User ID"
<< std::setw(25) << "Username" << std::setw(25) << "Username"
<< std::setw(25) << "Full Name"
<< std::setw(25) << "User Type" << std::setw(25) << "User Type"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < activeUserCount; iterator++) for (int iterator = 0; iterator < activeUserCount; iterator++)
@@ -685,6 +866,7 @@ inline void displayAllActiveUsers(util::Map<std::string, const User*>& activeUse
std::cout << std::left << std::setw(10) << (iterator + 1) std::cout << std::left << std::setw(10) << (iterator + 1)
<< std::setw(15) << user->getId() << std::setw(15) << user->getId()
<< std::setw(25) << user->getUserName() << std::setw(25) << user->getUserName()
<< std::setw(25) << user->getName()
<< std::setw(25) << util::getUserTypeString(user->getUserType()) << std::setw(25) << util::getUserTypeString(user->getUserType())
<< std::endl; << std::endl;
} }
@@ -697,6 +879,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 Function: selectServiceFromServices
Description: Displays active services and allows the customer to select one by index. Description: Displays active services and allows the customer to select one by index.
@@ -705,6 +909,12 @@ Return type: const Service* - selected service
*/ */
inline const Service* selectServiceFromServices(const util::Map<std::string, const Service*>& services) 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;
}
std::cout << std::endl;
util::Map<int, const Service*> activeServicesMap; util::Map<int, const Service*> activeServicesMap;
int currentIndex = 1; int currentIndex = 1;
int userInputIndex; int userInputIndex;
@@ -751,6 +961,31 @@ inline const Service* selectServiceFromServices(const util::Map<std::string, con
return activeServicesMap[userInputIndex]; return activeServicesMap[userInputIndex];
} }
/*
Function: filterComboPackages
Description:
Filters the given list of combo packages and returns only those that are ACTIVE.
Parameters:
- comboPackages: util::Map<std::string, const ComboPackage*>&
Map of combo package IDs to ComboPackage pointers.
Returns:
- util::Map<std::string, const ComboPackage*>
Map containing only active combo packages.
*/
inline util::Map<std::string, const ComboPackage*> filterComboPackages(util::Map<std::string, const ComboPackage*>& comboPackages)
{
util::Map<std::string, const ComboPackage*> activeComboPackages;
for (int iterator = 0; iterator < comboPackages.getSize(); iterator++)
{
const ComboPackage* currentComboPackage = comboPackages.getValueAt(iterator);
if (currentComboPackage && currentComboPackage->getState() == util::State::ACTIVE)
{
activeComboPackages.insert(currentComboPackage->getId(), currentComboPackage);
}
}
return activeComboPackages;
}
/* /*
Function: selectComboPackageFromPackages Function: selectComboPackageFromPackages
Description: Displays active combo packages and allows the customer to select one by index. Description: Displays active combo packages and allows the customer to select one by index.
@@ -762,10 +997,11 @@ inline const ComboPackage* selectComboPackageFromPackages(const util::Map<std::s
util::Map<int, const ComboPackage*> activeComboPackages; util::Map<int, const ComboPackage*> activeComboPackages;
int currentIndex = 1; int currentIndex = 1;
int userInputIndex; int userInputIndex;
std::cout << std::endl;
std::cout << std::left std::cout << std::left
<< std::setw(10) << "Index" << std::setw(10) << "Index"
<< std::setw(15) << "Combo Package ID" << std::setw(15) << "Combo ID"
<< std::setw(15) << "Combo Package Name" << std::setw(35) << "Combo Name"
<< std::setw(15) << "Estimate Cost" << std::setw(15) << "Estimate Cost"
<< std::endl; << std::endl;
for (int index = 0; index < comboPackages.getSize(); index++) for (int index = 0; index < comboPackages.getSize(); index++)
@@ -779,7 +1015,7 @@ inline const ComboPackage* selectComboPackageFromPackages(const util::Map<std::s
std::cout << std::left std::cout << std::left
<< std::setw(10) << currentIndex << std::setw(10) << currentIndex
<< std::setw(15) << currentComboPackage->getId() << std::setw(15) << currentComboPackage->getId()
<< std::setw(25) << currentComboPackage->getPackageName() << std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30)
<< std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage) << std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage)
<< std::endl; << std::endl;
currentIndex++; currentIndex++;
@@ -847,7 +1083,7 @@ inline util::Map<std::string, const InventoryItem*> filterActiveItems(const util
for (int index = 0; index < inventorySize; index++) for (int index = 0; index < inventorySize; index++)
{ {
const InventoryItem* item = inventoryItems.getValueAt(index); const InventoryItem* item = inventoryItems.getValueAt(index);
if (item != nullptr && item->getState() != util::State::INACTIVE) if (item && item->getState() != util::State::INACTIVE)
{ {
activeItems.insert(item->getId(), item); activeItems.insert(item->getId(), item);
} }
@@ -885,6 +1121,7 @@ inline void displayInventoryWithItems(util::Map<std::string, const InventoryItem
<< std::endl; << std::endl;
} }
} }
std::cout << std::endl;
} }
/* /*
@@ -904,7 +1141,7 @@ inline void addQuantityToItem(util::Map<std::string, const InventoryItem*>& inve
int activeSize = activeItems.getSize(); int activeSize = activeItems.getSize();
if (activeSize == 0) 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; return;
} }
displayInventoryWithItems(activeItems); displayInventoryWithItems(activeItems);
@@ -912,7 +1149,7 @@ inline void addQuantityToItem(util::Map<std::string, const InventoryItem*>& inve
util::read(itemIndex); util::read(itemIndex);
if (itemIndex < 1 || itemIndex > activeSize) if (itemIndex < 1 || itemIndex > activeSize)
{ {
std::cout << "Invalid index selected." << std::endl; std::cout << "\nInvalid index selected." << std::endl << std::endl;
return; return;
} }
std::cout << "Enter quantity to add: "; std::cout << "Enter quantity to add: ";
@@ -927,13 +1164,14 @@ inline void addQuantityToItem(util::Map<std::string, const InventoryItem*>& inve
{ {
std::string selectedItemId = selectedItem->getId(); std::string selectedItemId = selectedItem->getId();
m_controller.addInventoryItemStock(selectedItemId, quantity); m_controller.addInventoryItemStock(selectedItemId, quantity);
std::cout << "Updated " << selectedItem->getPartName() std::cout << "\nUpdated " << selectedItem->getPartName()
<< " stock. New quantity: " << selectedItem->getQuantity() << " stock. New quantity: " << selectedItem->getQuantity()
<< std::endl
<< std::endl; << std::endl;
} }
else 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;
} }
} }
@@ -957,14 +1195,14 @@ inline void displayComboPackagesWithIndex(util::Map<int, const ComboPackage*>& c
std::cout << std::left std::cout << std::left
<< std::setw(8) << "Index" << std::setw(8) << "Index"
<< std::setw(10) << "ID" << std::setw(10) << "ID"
<< std::setw(20) << "Package Name" << std::setw(35) << "Package Name"
<< std::setw(15) << "Discount (%)" << std::setw(15) << "Discount (%)"
<< "\n"; << "\n";
} }
std::cout << std::left std::cout << std::left
<< std::setw(8) << currentComboPackageIndexMap.getKeyAt(iterator) << std::setw(8) << currentComboPackageIndexMap.getKeyAt(iterator)
<< std::setw(10) << currentComboPackage->getId() << std::setw(10) << currentComboPackage->getId()
<< std::setw(20) << currentComboPackage->getPackageName() << std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30)
<< std::setw(15) << currentComboPackage->getDiscountPercentage() << std::setw(15) << currentComboPackage->getDiscountPercentage()
<< "\n"; << "\n";
} }
@@ -981,7 +1219,8 @@ inline std::string selectComboPackage(util::Map<std::string, const ComboPackage*
util::Map<int, const ComboPackage*> currentComboPackageIndexMap; util::Map<int, const ComboPackage*> currentComboPackageIndexMap;
if (currentComboPackages.getSize() == 0) 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; int currentIndex = 1, choice, selectedIndex;
for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++) for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++)
@@ -994,7 +1233,8 @@ inline std::string selectComboPackage(util::Map<std::string, const ComboPackage*
} }
if (currentComboPackageIndexMap.getSize() == 0) if (currentComboPackageIndexMap.getSize() == 0)
{ {
throw std::runtime_error("No combo packages currently active."); std::cout << "No combo packages currently active.\n";
return "";
} }
displayComboPackagesWithIndex(currentComboPackageIndexMap); displayComboPackagesWithIndex(currentComboPackageIndexMap);
std::cout << "Enter your choice(Index): "; std::cout << "Enter your choice(Index): ";
@@ -6,6 +6,7 @@ Description: Implementation file containing the method definitions of the
Author: Trenser Author: Trenser
Date:19-May-2026 Date:19-May-2026
*/ */
#include "Enums.h" #include "Enums.h"
#include "InputHelper.h" #include "InputHelper.h"
#include "JobCard.h" #include "JobCard.h"
@@ -92,18 +93,15 @@ Returns:
*/ */
void TechnicianMenu::completeJob() void TechnicianMenu::completeJob()
{ {
util::clear();
std::cout << "Complete Job\n";
util::Map<std::string, const JobCard*> assignedJobCards = m_controller.getJobCardsByUser(); util::Map<std::string, const JobCard*> assignedJobCards = m_controller.getJobCardsByUser();
util::Map<int, const JobCard*> incompleteJobCards; util::Map<std::string, const JobCard*> startedJobCards = filterStartedJobCards(assignedJobCards);
std::cout << "Jobs to be completed.\n"; std::string selectedJobID = selectJobCardToComplete(startedJobCards);
std::string selectedJobID = selectJobCardToComplete(assignedJobCards, incompleteJobCards); if (!selectedJobID.empty())
if (selectedJobID == "")
{
std::cout << "Failed to complete the job.\n";
}
else
{ {
m_controller.completeJob(selectedJobID); m_controller.completeJob(selectedJobID);
std::cout << "Job marked as completed.\n"; std::cout << "\nJob marked as completed.\n\n";
} }
util::pressEnter(); util::pressEnter();
} }
@@ -7,6 +7,8 @@ Author: Trenser
Date:19-May-2026 Date:19-May-2026
*/ */
#include <iostream>
#include <stdexcept>
#include "Enums.h" #include "Enums.h"
#include "InputHelper.h" #include "InputHelper.h"
#include "OutputHelper.h" #include "OutputHelper.h"
@@ -22,6 +24,8 @@ Parameter: None
Return type: void Return type: void
*/ */
void UserInterface::run() void UserInterface::run()
{
try
{ {
m_controller.loadSystemData(); m_controller.loadSystemData();
m_controller.runSystemChecks(); m_controller.runSystemChecks();
@@ -47,6 +51,19 @@ void UserInterface::run()
} }
m_controller.saveSystemData(); 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;
}
}
/* /*
Function: handleOperation Function: handleOperation
@@ -85,6 +102,7 @@ void UserInterface::login()
{ {
std::string username, password; std::string username, password;
util::clear(); util::clear();
std::cout << "Login\n";
std::cout << "Enter username: "; std::cout << "Enter username: ";
util::read(username); util::read(username);
std::cout << "Enter password: "; std::cout << "Enter password: ";
@@ -92,7 +110,7 @@ void UserInterface::login()
if (m_controller.login(username, password)) if (m_controller.login(username, password))
{ {
const User* authenticatedUser = m_controller.getAuthenticatedUser(); const User* authenticatedUser = m_controller.getAuthenticatedUser();
if (authenticatedUser != nullptr) if (authenticatedUser && authenticatedUser->getState() != util::State::INACTIVE)
{ {
switch (authenticatedUser->getUserType()) switch (authenticatedUser->getUserType())
{ {
@@ -110,10 +128,16 @@ void UserInterface::login()
break; break;
} }
} }
else if (authenticatedUser && authenticatedUser->getState() == util::State::INACTIVE)
{
std::cout << "\nError: Your account has been disabled. Please contact your Administrator.";
util::pressEnter();
}
} }
else else
{ {
std::cout << "\nError: Invalid Username or Password"; std::cout << "\nError: Invalid Username or Password";
util::pressEnter();
} }
} }
@@ -129,6 +153,7 @@ void UserInterface::registerCustomer()
{ {
std::string username, name, email, phone, password; std::string username, name, email, phone, password;
util::clear(); util::clear();
std::cout << "Register Customer\n";
std::cout << "Enter username: "; std::cout << "Enter username: ";
util::read(username); util::read(username);
std::cout << "Enter name: "; std::cout << "Enter name: ";