Compare commits

..

37 Commits

Author SHA1 Message Date
Jissin Mathew ce50467816 Fix Technician Job Status Update Screen UI and Formatting Issues
- Corrected inconsistent status label formatting: replaced "Inprogress" with "In Progress" in TechnicianMenu and MenuHelper.
- Updated headings in selectJobCardToUpdate to clearer phrasing:
  - "Select a job to mark as In Progress"
  - "Select a job to mark as Completed".
- Added spacing before and after the "No jobs available" message to improve readability and provide clear separation from headings.
- Replaced duplicated prompt "Select the Job Card to Update (Index):" with concise "Enter the job index to update:".
- Improved TechnicianMenu option display to show "In Progress" instead of "Inprogress".

Fixes #1808
2026-06-01 18:02:45 +05:30
joelthomastrenser 17f24b7733 Merged PR 1155: Implement Confirm Payment Functionality
- Added Controller::getAllInvoices – retrieves all invoices from PaymentManagementService and returns them as a read-only map
- Implemented Controller::confirmPayment – delegates payment confirmation for a given invoice ID to PaymentManagementService
- Introduced PaymentManagementService::getAllInvoice – provides access to all invoices stored in the datastore
- Added PaymentManagementService::confirmPayment – confirms payment for a specific invoice, updates payment date and status, and sends notification
- Extended util::PaymentStatus enum – added PAID status and updated string conversion
- Integrated AdminMenu::confirmPayment – validates invoice list, filters by status, allows selection, and confirms payment
- Updated CustomerMenu::completePayments – uses parameterized status filtering for invoice selection
- Enhanced MenuHelper::selectInvoiceFromUserForPayment – accepts requiredStatus parameter for flexible filtering
- Adjusted AdminMenu options – added "Confirm Payment" before Logout

Related work items: #1797
2026-06-01 17:34:41 +05:30
Avinash Rajesh 1032fc64bd Commit aee6356e: Implement Confirm Payment Functionality
<User Story> Complete Payments - 1797</User Story>

<Changes>
1. Added Controller::getAllInvoices
   - Retrieves all invoices from PaymentManagementService and returns them as a read-only map.
2. Implemented Controller::confirmPayment
   - Delegates payment confirmation for a given invoice ID to PaymentManagementService.
3. Introduced PaymentManagementService::getAllInvoice
   - Provides access to all invoices stored in the datastore.
4. Added PaymentManagementService::confirmPayment
   - Confirms payment for a specific invoice, updates payment date and status, and sends notification.
5. Extended util::PaymentStatus enum
   - Added PAID status and updated string conversion.
6. Integrated AdminMenu::confirmPayment
   - Validates invoice list, filters by status, allows selection, and confirms payment.
7. Updated CustomerMenu::completePayments
   - Uses parameterized status filtering for invoice selection.
8. Enhanced MenuHelper::selectInvoiceFromUserForPayment
   - Accepts requiredStatus parameter for flexible filtering.
9. Adjusted AdminMenu options
   - Added "Confirm Payment" before Logout.
</Changes>

<Test>
Acceptance Criteria:
1. Admin selects "Confirm Payment" from menu.
   - Verify system prompts and displays invoices filtered by status.
2. Admin selects invoice with status = PAID.
   - Verify payment confirmation updates date, sets status, and sends notification.
3. Admin attempts confirmation with empty invoice list.
   - Verify error message: "No pending invoices available for confirmation."
4. Customer completes payment.
   - Verify selection uses util::PaymentStatus::PENDING and payment flow works correctly.
5. Invalid invoice ID entered.
   - Verify system throws runtime_error with "Payment failed: invalid invoice ID."

Precondition:
1. Admin logged into system.
2. At least one invoice exists in datastore.
3. Notification system available.

Steps:
1. Navigate to Admin menu → Confirm Payment.
2. Select invoice with PAID status.
3. Confirm payment and check notification.
4. Attempt with empty invoice list.
5. Attempt with invalid invoice ID.
</Test>

<Review>
Sreeja Reghukumar
</Review>
2026-06-01 17:30:27 +05:30
joelthomastrenser cfd1a2b675 Merged PR 1154: Implement Update Job Status for technician
- Renamed Controller and ServiceManagementService methods from completeJob to updateJobStatus for clarity and flexibility.
- Enhanced ServiceManagementService::updateJobStatus to support transitions:
- STARTED → INPROGRESS
- INPROGRESS → COMPLETED (with invoice generation and customer notification).
- Added INPROGRESS state to ServiceJobStatus enum and updated string conversion utilities.
- Introduced filterJobCards helper to generalize job filtering by status.
- Updated TechnicianMenu to allow technicians to select job type (Started/Inprogress) and update status accordingly.
- Improved job display to show current status and truncated service names for readability.

Related work items: #1798
2026-06-01 17:25:28 +05:30
Jissin Mathew 70ec47df04 Fix review comments 2026-06-01 16:49:57 +05:30
Jissin Mathew 2ea77bf9b6 Implement Update Job Status for technician
<UserStory> SER1798: Update Job Status </UserStory>

<Changes>
    1. Renamed Controller and ServiceManagementService methods from completeJob to updateJobStatus for clarity and flexibility.
    2. Enhanced ServiceManagementService::updateJobStatus to support transitions:
       - STARTED → INPROGRESS
       - INPROGRESS → COMPLETED (with invoice generation and customer notification).
    3. Added INPROGRESS state to ServiceJobStatus enum and updated string conversion utilities.
    4. Introduced filterJobCards helper to generalize job filtering by status.
    5. Updated TechnicianMenu to allow technicians to select job type (Started/Inprogress) and update status accordingly.
    6. Improved job display to show current status and truncated service names for readability.
</Changes>

<Test>

 Acceptance Criteria:
 1. Technician can select a job with status STARTED and update it to INPROGRESS.
 2. Technician can select a job with status INPROGRESS and update it to COMPLETED.
 3. Completed bookings automatically generate invoices and send notifications to customers.
 4. Job status updates are reflected in the technician’s job list and customer view.

 Precondition:
 1. Technician is logged into the system.
 2. Assigned job cards exist with valid statuses (STARTED or INPROGRESS).
 3. Datastore and payment service are available.

 Steps:
 1. Navigate to Technician menu and choose "Update Job Status".
    - Verify that the system prompts for job type selection (Started/Inprogress).
 2. Select a job card from the filtered list.
    - Verify that the job card details are displayed with status.
 3. Confirm update.
    - Verify that the job status changes correctly.
 4. For jobs updated to COMPLETED:
    - Verify that the booking status is updated, invoice is generated, and notification is sent.
</Test>

<Review>
Sreeja Reghukumar, please review
</Review>

#1798
2026-06-01 16:49:56 +05:30
joelthomastrenser 972e353832 Merged PR 1152: Vehicle Service System v1.0.0.0
Features Included:

Secure access and session management with role-based authentication
Customer and technician account management
Service booking and technician assignment
Service and combo package management
Job card tracking and completion workflow
Inventory management and stock monitoring
Invoice generation and payment processing
Notification management with configurable preferences
Observer pattern based alerts for inventory, payments, and service updates
2026-06-01 16:48:25 +05:30
joelthomastrenser c5ada405e6 Merged PR 1148: Vehicle Service System
**Features Included:**
- Secure access and session management with role-based authentication
- Customer and technician account management
- Service booking and technician assignment
- Service and combo package management
- Job card tracking and completion workflow
- Inventory management and stock monitoring
- Invoice generation and payment processing
- Notification management with configurable preferences
- Observer pattern based alerts for inventory, payments, and service updates

Related work items: #1551, #1552, #1553, #1560, #1561, #1563, #1564, #1568, #1569, #1570, #1571, #1572, #1573, #1574, #1575, #1576, #1577, #1578, #1579, #1580, #1581, #1582, #1583, #1584, #1585, #1586, #1587, #1588, #1592, #1593, #1594, #1595, #1596, #1597, #1598, #1599, #1600, #1601, #1624, #1626, #1646, #1648, #1649, #1655, #1656, #1668, #1669, #1672, #1673, #1679, #1680, #1708, #1709, #1736, #1737, #1738, #1739, #1740, #1741, #1742, #1743, #1744, #1745, #1746, #1747, #1748, #1749, #1750, #1751, #1752, #1753, #1754, #1777, #1778, #1779, #1780, #1781, #1782, #1783, #1784, #1786, #1788, #1789, #1793
2026-05-29 11:50:03 +05:30
joelthomastrenser d6b4310de6 Merged PR 1142: Add display menus for users, services, combo packages and jobs
Add display menus for users, services, combo packages and jobs

Changes:
- Added display options for users, services, combo packages and technician jobs
- Updated admin and technician menu options and navigation
- Added reusable helper functions for displaying data in tabular format
- Improved user and combo package listing displays
- Fixed minor validation and error message formatting issues
2026-05-28 16:39:37 +05:30
joelthomastrenser 451085e9c2 Add display menus for users, services, combo packages and jobs
Changes:
- Added display options for users, services, combo packages and technician jobs
- Updated admin and technician menu options and navigation
- Added reusable helper functions for displaying data in tabular format
- Improved user and combo package listing displays
- Fixed minor validation and error message formatting issues
2026-05-28 16:36:34 +05:30
joelthomastrenser 4657d3e8d1 Merged PR 1139: Fix: create missing directories before file creation
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

Related work items: #1646, #1793
2026-05-28 10:35:54 +05:30
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
21 changed files with 1346 additions and 478 deletions
@@ -392,9 +392,9 @@ Parameters:
Returns: Returns:
- void - void
*/ */
void Controller::completeJob(const std::string& jobID) void Controller::updateJobStatus(const std::string& jobID)
{ {
m_serviceManagementService.completeJob(jobID); m_serviceManagementService.updateJobStatus(jobID);
} }
/* /*
@@ -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);
} }
@@ -464,6 +462,38 @@ util::Map<std::string, const Invoice*> Controller::getInvoicesByUser()
return userInvoicesReadOnly; return userInvoicesReadOnly;
} }
/*
Function: getAllInvoices
Description: Retrieves all invoices from the PaymentManagementService and returns them as a read-only map.
Parameters:
- none
Returns:
- util::Map<std::string, const Invoice*>: Map of invoice IDs to invoice objects
*/
util::Map<std::string, const Invoice*> Controller::getAllInvoices()
{
auto invoices = m_paymentManagementService.getAllInvoices();
util::Map<std::string, const Invoice*> readOnlyInvoice;
for (int iterator = 0; iterator < invoices.getSize(); iterator++)
{
readOnlyInvoice.insert(invoices.getKeyAt(iterator), invoices.getValueAt(iterator));
}
return readOnlyInvoice;
}
/*
Function: confirmPayment
Description: Delegates payment confirmation for a given invoice ID to the PaymentManagementService.
Parameters:
- invoiceID: std::string, ID of the invoice to confirm
Returns:
- void
*/
void Controller::confirmPayment(const std::string& invoiceID)
{
m_paymentManagementService.confirmPayment(invoiceID);
}
/* /*
Function: completePayment Function: completePayment
Description: Completes payment for a specific invoice using the given payment mode. Description: Completes payment for a specific invoice using the given payment mode.
@@ -59,11 +59,13 @@ public:
void createService(const std::string& name, const util::Vector<std::string>& inventoryItemIDs, double laborCost); void createService(const std::string& name, const util::Vector<std::string>& inventoryItemIDs, double laborCost);
void removeService(const std::string& serviceID); void removeService(const std::string& serviceID);
util::Map<std::string, const JobCard*> getJobCardsByUser(); util::Map<std::string, const JobCard*> getJobCardsByUser();
void completeJob(const std::string& jobID); void updateJobStatus(const std::string& jobID);
void removeUser(const std::string& userID); void removeUser(const std::string& userID);
void createComboPackage(const std::string& name, const util::Vector<std::string>& serviceIDs, double discountPercentage); void createComboPackage(const std::string& name, const util::Vector<std::string>& serviceIDs, double discountPercentage);
void removeComboPackage(const std::string& comboPackageID); void removeComboPackage(const std::string& comboPackageID);
util::Map<std::string, const Invoice*> getInvoicesByUser(); util::Map<std::string, const Invoice*> getInvoicesByUser();
util::Map<std::string, const Invoice*> getAllInvoices();
void confirmPayment(const std::string& invoiceID);
void completePayment(const std::string& invoiceID, util::PaymentMode paymentMode); void completePayment(const std::string& invoiceID, util::PaymentMode paymentMode);
util::Vector<const Notification*> getNotifications(); util::Vector<const Notification*> getNotifications();
void deleteNotification(const std::string& notificationID); void deleteNotification(const std::string& notificationID);
@@ -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),
@@ -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);
} }
@@ -367,15 +368,15 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti
if (invoiceIndex != -1) if (invoiceIndex != -1)
{ {
Invoice* invoice = currentInvoices.getValueAt(invoiceIndex); Invoice* invoice = currentInvoices.getValueAt(invoiceIndex);
if (invoice && invoice->getStatus() != util::PaymentStatus::COMPLETED) if (invoice && invoice->getStatus() != util::PaymentStatus::PAID)
{ {
User* currentUser = invoice->getBooking()->getCustomer(); User* currentUser = invoice->getBooking()->getCustomer();
invoice->setPaymentMethod(paymentMode); invoice->setPaymentMethod(paymentMode);
invoice->setPaymentDate(util::Timestamp()); invoice->setPaymentDate(util::Timestamp());
invoice->setStatus(util::PaymentStatus::COMPLETED); invoice->setStatus(util::PaymentStatus::PAID);
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);
} }
} }
@@ -384,3 +385,47 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti
throw std::runtime_error("Payment failed: invalid invoice ID."); throw std::runtime_error("Payment failed: invalid invoice ID.");
} }
} }
/*
Function: getAllInvoice
Description: Provides access to all invoices stored in the data store.
Parameters:
- none
Returns:
- util::Map<std::string, Invoice*>&: Map of invoice IDs to invoice objects
*/
util::Map<std::string, Invoice*>& PaymentManagementService::getAllInvoices()
{
return m_dataStore.getInvoices();
}
/*
Function: confirmPayment
Description: Confirms payment for a specific invoice. Updates payment date and status,
then sends a notification to the customer.
Parameters:
- invoiceID: std::string, ID of the invoice to confirm
Returns:
- void
Throws:
- std::runtime_error if the invoice ID is invalid
*/
void PaymentManagementService::confirmPayment(const std::string& invoiceID)
{
auto& currentInvoices = m_dataStore.getInvoices();
int invoiceIndex = currentInvoices.find(invoiceID);
if (invoiceIndex == -1)
{
throw std::runtime_error("Payment confirmation failed: invalid invoice ID.");
}
Invoice* invoice = currentInvoices.getValueAt(invoiceIndex);
if (!invoice || invoice->getStatus() != util::PaymentStatus::PAID)
{
throw std::runtime_error("Payment confirmation failed: invoice is not awaiting confirmation.");
}
User* currentUser = invoice->getBooking()->getCustomer();
invoice->setStatus(util::PaymentStatus::COMPLETED);
std::string title = "Payment Confirmed";
std::string message = "Payment Confirmed for Invoice ID " + invoiceID;
sendNotification(currentUser, title, message);
}
@@ -28,6 +28,8 @@ public:
void generateInvoice(ServiceBooking* booking); void generateInvoice(ServiceBooking* booking);
util::Map<std::string, Invoice*> getInvoices(const std::string& customerID); util::Map<std::string, Invoice*> getInvoices(const std::string& customerID);
void completePayment(const std::string& invoiceID, util::PaymentMode paymentMode); void completePayment(const std::string& invoiceID, util::PaymentMode paymentMode);
util::Map<std::string, Invoice*>& getAllInvoices();
void confirmPayment(const std::string& invoiceID);
void sendPaymentReminders(); void sendPaymentReminders();
void sendNotification(User* user, const std::string& title, const std::string& message) override; void sendNotification(User* user, const std::string& title, const std::string& message) override;
void attach(User* user) override; void attach(User* user) override;
@@ -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"
@@ -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");
@@ -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
{ {
@@ -981,7 +1080,7 @@ static bool hasCompletedAllJobs(std::string bookingId, util::Map<std::string, Jo
JobCard* currentJob = currentAssignedJobs.getValueAt(iterator); JobCard* currentJob = currentAssignedJobs.getValueAt(iterator);
if (currentJob->getBookingId() == bookingId) if (currentJob->getBookingId() == bookingId)
{ {
if (currentJob->getStatus() == util::ServiceJobStatus::STARTED) if (currentJob->getStatus() != util::ServiceJobStatus::COMPLETED && currentJob->getStatus() != util::ServiceJobStatus::CANCELLED)
{ {
return false; return false;
} }
@@ -991,18 +1090,19 @@ static bool hasCompletedAllJobs(std::string bookingId, util::Map<std::string, Jo
} }
/* /*
Function: completeJob Function: updateJobStatus
Description: Marks a job card as completed for the authenticated technician. Description:
If all job cards in the booking are completed, marks the booking as completed Updates the status of a job card assigned to the currently authenticated technician.
and generates an invoice. - If the job is STARTED, it moves to IN_PROGRESS.
- If the job is IN_PROGRESS, it moves to COMPLETED.
When all jobs in a service booking are completed, the booking status is updated,
an invoice is generated, and a notification is sent to the customer.
Parameters: Parameters:
- jobID: std::string, ID of the job card - jobID: const std::string&, unique identifier of the job card to update.
Returns: Returns:
- void - void
Throws:
- std::runtime_error if technician is not authenticated, job card not found, or job already completed
*/ */
void ServiceManagementService::completeJob(const std::string& jobID) void ServiceManagementService::updateJobStatus(const std::string& jobID)
{ {
AuthenticationManagementService authenticationManagementService; AuthenticationManagementService authenticationManagementService;
PaymentManagementService paymentManagementService; PaymentManagementService paymentManagementService;
@@ -1027,26 +1127,30 @@ void ServiceManagementService::completeJob(const std::string& jobID)
} }
if (currentJob->getStatus() == util::ServiceJobStatus::STARTED) if (currentJob->getStatus() == util::ServiceJobStatus::STARTED)
{ {
currentJob->setStatus(util::ServiceJobStatus::COMPLETED); currentJob->setStatus(util::ServiceJobStatus::IN_PROGRESS);
jobStatusUpdated = true; jobStatusUpdated = true;
} }
} else if (currentJob->getStatus() == util::ServiceJobStatus::IN_PROGRESS)
else
{ {
throw std::runtime_error("Failed to complete the job, some error occured or job already completed."); currentJob->setStatus(util::ServiceJobStatus::COMPLETED);
} jobStatusUpdated = true;
if (!jobStatusUpdated)
{
throw std::runtime_error("Failed to complete the job, some error occured or job already completed.");
}
serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs); serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs);
if (serviceBookingCompleted) if (serviceBookingCompleted)
{ {
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);
} }
} }
}
else
{
throw std::runtime_error("Failed to update job status. Job may already be completed.");
}
if (!jobStatusUpdated)
{
throw std::runtime_error("Failed to update job status. Job may already be completed.");
}
}
@@ -37,7 +37,7 @@ public:
void createService(const std::string& name, const util::Vector<std::string>& inventoryItemIDs, double laborCost); void createService(const std::string& name, const util::Vector<std::string>& inventoryItemIDs, double laborCost);
void removeService(const std::string& serviceID); void removeService(const std::string& serviceID);
util::Map<std::string, JobCard*> getJobCards(const std::string& technicianID); util::Map<std::string, JobCard*> getJobCards(const std::string& technicianID);
void completeJob(const std::string& jobID); void updateJobStatus(const std::string& jobID);
void cancelCustomerServiceBookings(const std::string& customerID); void cancelCustomerServiceBookings(const std::string& customerID);
void cancelTechnicianJobs(const std::string& technicianID); void cancelTechnicianJobs(const std::string& technicianID);
void createComboPackage(const std::string& packageName, const util::Vector<std::string>& serviceIDs, double discountPercentage); void createComboPackage(const std::string& packageName, const util::Vector<std::string>& serviceIDs, double discountPercentage);
@@ -297,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);
} }
} }
} }
@@ -315,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);
} }
@@ -29,7 +29,8 @@ namespace util
enum class PaymentStatus enum class PaymentStatus
{ {
PENDING, PENDING,
COMPLETED COMPLETED,
PAID
}; };
enum class ServiceJobStatus enum class ServiceJobStatus
@@ -37,6 +38,7 @@ namespace util
PENDING, PENDING,
STARTED, STARTED,
COMPLETED, COMPLETED,
IN_PROGRESS,
CANCELLED CANCELLED
}; };
@@ -137,6 +139,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");
} }
@@ -156,6 +162,8 @@ namespace util
return "PENDING"; return "PENDING";
case PaymentStatus::COMPLETED: case PaymentStatus::COMPLETED:
return "COMPLETED"; return "COMPLETED";
case PaymentStatus::PAID:
return "PAID";
} }
throw std::invalid_argument("Invalid PaymentStatus"); throw std::invalid_argument("Invalid PaymentStatus");
} }
@@ -205,6 +213,8 @@ namespace util
return "COMPLETED"; return "COMPLETED";
case ServiceJobStatus::CANCELLED: case ServiceJobStatus::CANCELLED:
return "CANCELLED"; return "CANCELLED";
case ServiceJobStatus::IN_PROGRESS:
return "IN_PROGRESS";
} }
throw std::invalid_argument("Invalid ServiceJobStatus"); throw std::invalid_argument("Invalid ServiceJobStatus");
} }
@@ -237,6 +247,10 @@ namespace util
{ {
return ServiceJobStatus::CANCELLED; return ServiceJobStatus::CANCELLED;
} }
if (value == "IN_PROGRESS")
{
return ServiceJobStatus::IN_PROGRESS;
}
throw std::invalid_argument("Invalid ServiceJobStatus string"); throw std::invalid_argument("Invalid ServiceJobStatus 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);
@@ -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) + "...";
}
} }
@@ -42,15 +42,19 @@ void AdminMenu::showMenu()
<< "\n3. Remove Inventory Item" << "\n3. Remove Inventory Item"
<< "\n4. Check Stock Availability" << "\n4. Check Stock Availability"
<< "\n5. Assign Job to Technician" << "\n5. Assign Job to Technician"
<< "\n6. Add Technician" << "\n6. Display Users"
<< "\n7. Remove Customer/Technician" << "\n7. Add Technician"
<< "\n8. Create Service" << "\n8. Remove Customer/Technician"
<< "\n9. Remove Service" << "\n9. Display Services"
<< "\n10. Create Combo Package" << "\n10. Create Service"
<< "\n11. Remove Combo Package" << "\n11. Remove Service"
<< "\n12. View Notifications" << "\n12. Display Combo Packages"
<< "\n13. Change Password" << "\n13. Create Combo Package"
<< "\n14. Logout" << "\n14. Remove Combo Package"
<< "\n15. View Notifications"
<< "\n16. Change Password"
<< "\n17. Confirm Payment"
<< "\n18. Logout"
<< "\nEnter a choice: "; << "\nEnter a choice: ";
util::read(choice); util::read(choice);
if (!handleOperation(choice)) if (!handleOperation(choice))
@@ -92,30 +96,42 @@ bool AdminMenu::handleOperation(int choice)
assignJob(); assignJob();
break; break;
case 6: case 6:
addTechnician(); displayUsers();
break; break;
case 7: case 7:
removeUser(); addTechnician();
break; break;
case 8: case 8:
createService(); removeUser();
break; break;
case 9: case 9:
removeService(); displayServices();
break; break;
case 10: case 10:
createComboPackages(); createService();
break; break;
case 11: case 11:
removeComboPackage(); removeService();
break; break;
case 12: case 12:
viewNotifications(); displayComboPackages();
break; break;
case 13: case 13:
changePassword(); createComboPackages();
break; break;
case 14: case 14:
removeComboPackage();
break;
case 15:
viewNotifications();
break;
case 16:
changePassword();
break;
case 17:
confirmPayment();
break;
case 18:
logout(); logout();
return false; return false;
default: default:
@@ -218,13 +234,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 : ";
@@ -232,15 +250,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();
} }
@@ -255,6 +279,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();
@@ -333,14 +358,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)
@@ -356,14 +383,37 @@ 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();
}
/*
Function: displayServices()
Description: Display all active services
Parameters:
- None
Returns:
- void
*/
void AdminMenu::displayServices()
{
util::clear();
std::cout << "List of all Services\n";
util::Map<std::string, const Service*> currentServices = m_controller.getServices();
util::Map<std::string, const Service*> currentActiveServices = filterActiveServices(currentServices);
displayAllServices(currentActiveServices);
util::pressEnter(); util::pressEnter();
} }
@@ -378,17 +428,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();
} }
@@ -403,21 +460,94 @@ 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();
} }
/*
Function: displayUsers
Description: Displays all users.
Parameter: None
Return type: void
*/
void AdminMenu::displayUsers()
{
util::clear();
auto listOfUsers = m_controller.getUsers();
auto listOfActiveUsers = filterActiveUsers(listOfUsers);
int activeUserCount = listOfActiveUsers.getSize();
std::cout << "List of all Users\n";
if (activeUserCount < 1)
{
std::cout << "No Active users." << std::endl;
util::pressEnter();
return;
}
displayAllUsers(listOfActiveUsers);
util::pressEnter();
}
/*
Function: confirmPayment
Description: Confirms payment for a selected invoice. Validates invoice status, updates payment date,
sets status to COMPLETED, and sends a notification to the customer.
Parameters:
- invoiceID: std::string, ID of the invoice to confirm
Returns:
- void
*/
void AdminMenu::confirmPayment()
{
util::clear();
std::cout << "Confirm Payment\n";
auto invoiceList = m_controller.getAllInvoices();
if (invoiceList.isEmpty())
{
std::cout << "No pending invoices available for confirmation.";
util::pressEnter();
return;
}
bool hasConfirmableInvoice = false;
for (int index = 0; index < invoiceList.getSize(); ++index)
{
const Invoice* invoice = invoiceList.getValueAt(index);
if (invoice && invoice->getStatus() == util::PaymentStatus::PAID)
{
hasConfirmableInvoice = true;
break;
}
}
if (!hasConfirmableInvoice)
{
std::cout << "No invoices awaiting confirmation.\n";
util::pressEnter();
return;
}
std::string selectedID = selectInvoiceFromUserForPayment(invoiceList, util::PaymentStatus::PAID);
if (selectedID == "")
{
std::cout << "Payment failed.\n";
util::pressEnter();
return;
}
m_controller.confirmPayment(selectedID);
std::cout << "Payment Confirmed successfully.\n";
util::pressEnter();
}
/* /*
Function: addTechnician Function: addTechnician
Description: Adds a new technician after validating username, password, email, and phone number. Description: Adds a new technician after validating username, password, email, and phone number.
@@ -428,36 +558,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();
} }
@@ -481,12 +612,12 @@ void AdminMenu::removeUser()
util::pressEnter(); util::pressEnter();
return; return;
} }
displayAllActiveUsers(listOfActiveUsers, activeUserCount); displayAllUsers(listOfActiveUsers);
std::cout << "Enter the index of the user to delete : "; std::cout << "Enter the index of the user to delete : ";
util::read(indexChoice); util::read(indexChoice);
if (indexChoice < 1 || indexChoice > activeUserCount) if (indexChoice < 1 || indexChoice > activeUserCount)
{ {
std::cout << "Error Invaild index.\n" << std::endl; std::cout << "Error invalid index.\n" << std::endl;
util::pressEnter(); util::pressEnter();
return; return;
} }
@@ -500,6 +631,24 @@ void AdminMenu::removeUser()
util::pressEnter(); util::pressEnter();
} }
/*
Function: displayComboPackages()
Description: Display all active combo packages
Parameters:
- None
Returns:
- void
*/
void AdminMenu::displayComboPackages()
{
util::clear();
std::cout << "List of all Combo Packages\n";
util::Map<std::string, const ComboPackage*> currentComboPackages = m_controller.getComboPackages();
util::Map<std::string, const ComboPackage*> currentActiveComboPackages = filterComboPackages(currentComboPackages);
displayAllComboPackages(currentActiveComboPackages);
util::pressEnter();
}
/* /*
Function: createComboPackages Function: createComboPackages
Description: Creates a new combo package by selecting two active services and applying a discount. Description: Creates a new combo package by selecting two active services and applying a discount.
@@ -509,7 +658,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++)
@@ -517,10 +669,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;
} }
@@ -535,11 +687,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;
} }
} }
@@ -569,16 +729,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();
} }
@@ -24,10 +24,14 @@ public:
void removeInventoryItem(); void removeInventoryItem();
void checkStockAvailability(); void checkStockAvailability();
void assignJob(); void assignJob();
void displayServices();
void createService(); void createService();
void removeService(); void removeService();
void displayUsers();
void confirmPayment();
void addTechnician(); void addTechnician();
void removeUser(); void removeUser();
void displayComboPackages();
void createComboPackages(); void createComboPackages();
void removeComboPackage(); void removeComboPackage();
void viewNotifications(); void viewNotifications();
@@ -220,20 +220,24 @@ void CustomerMenu::selectComboPackage()
util::clear(); util::clear();
std::cout << "Select a Combo Package\n"; std::cout << "Select a Combo Package\n";
auto comboPackages = m_controller.getComboPackages(); auto comboPackages = m_controller.getComboPackages();
if (comboPackages.isEmpty()) util::Map<std::string, const ComboPackage*> activeComboPackages = filterComboPackages(comboPackages);
if (activeComboPackages.isEmpty())
{ {
std::cout << "No combo packages available!"; std::cout << "No combo packages available!\n\n";
util::pressEnter(); util::pressEnter();
return; return;
} }
const ComboPackage* selectedComboPackage = selectComboPackageFromPackages(comboPackages); const ComboPackage* selectedComboPackage = selectComboPackageFromPackages(activeComboPackages);
if (selectedComboPackage == nullptr) if (selectedComboPackage == nullptr)
{ {
std::cout << "Failed to book combo package!"; std::cout << "Failed to book combo package!\n\n";
util::pressEnter(); util::pressEnter();
return; 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: ";
@@ -241,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();
} }
@@ -334,7 +338,7 @@ void CustomerMenu::completePayments()
util::pressEnter(); util::pressEnter();
return; return;
} }
std::string selectedID = selectInvoiceFromUserForPayment(currentInvoices); std::string selectedID = selectInvoiceFromUserForPayment(currentInvoices, util::PaymentStatus::PENDING);
if (selectedID == "") if (selectedID == "")
{ {
std::cout << "Payment failed.\n"; std::cout << "Payment failed.\n";
@@ -358,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();
@@ -29,6 +29,41 @@ Date: 21-May-2026
#include "Validator.h" #include "Validator.h"
#include "Vector.h" #include "Vector.h"
/*
Function: displayAllServices
Description: Displays all active services
Parameters:
- currentServices: util::Map<std::string, const Service*>, available services
Returns:
- void;
*/
inline void displayAllServices(util::Map<std::string, const Service*> currentServices)
{
if (currentServices.getSize() == 0)
{
std::cout << "No Services Currently Available.\n";
return;
}
std::cout << std::left
<< std::setw(12) << "Service ID"
<< std::setw(35) << "Name"
<< std::setw(10) << "Labor Cost"
<< std::endl;
for (int iterator = 0; iterator < currentServices.getSize(); iterator++)
{
const Service* currentService = currentServices.getValueAt(iterator);
if (currentService == nullptr || currentService->getState() == util::State::INACTIVE)
{
continue;
}
std::cout << std::left
<< std::setw(12) << currentService->getId()
<< std::setw(35) << util::truncateString(currentService->getName(), 30)
<< std::setw(10) << currentService->getLaborCost()
<< std::endl;
}
}
/* /*
Function: selectServicesToRemove Function: selectServicesToRemove
Description: Allows selection of a service to remove by index. Description: Allows selection of a service to remove by index.
@@ -39,36 +74,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)
@@ -93,16 +126,19 @@ Returns:
*/ */
inline 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 +152,20 @@ inline 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 +176,54 @@ inline 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.
@@ -168,8 +236,12 @@ Returns:
*/ */
inline 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 +251,12 @@ inline 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 +267,9 @@ inline 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;
} }
@@ -223,15 +285,15 @@ Returns:
inline 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;
} }
} }
@@ -250,6 +312,7 @@ inline void listAvailableTechnicians(util::Map<std::string, const User*> current
{ {
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 +335,7 @@ inline 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";
} }
} }
@@ -287,60 +350,64 @@ Returns:
inline 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;
} }
} }
/* /*
Function: selectInvoiceFromUserForPayment Function: selectInvoiceFromUserForPayment
Description: Lists all pending invoices for the customer and allows selection by index. Description: Displays a list of invoices filtered by the required payment status.
Allows the user to select an invoice by index and returns the corresponding invoice ID.
Parameters: Parameters:
- currentInvoices: util::Map<std::string, const Invoice*>&, map of customer invoices - currentInvoices: const util::Map<std::string, const Invoice*>&,
map of all invoices keyed by invoice ID
- requiredStatus: util::PaymentStatus,
the status to filter invoices (e.g., PENDING, PAID, COMPLETED)
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 or invalid index
*/ */
inline std::string selectInvoiceFromUserForPayment(const util::Map<std::string, const Invoice*>& currentInvoices) inline std::string selectInvoiceFromUserForPayment(const util::Map<std::string, const Invoice*>& currentInvoices, util::PaymentStatus requiredStatus)
{ {
util::clear();
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) << "Technician ID" << std::setw(18) << "Technician ID"
<< std::setw(20) << "Technician Name" << 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++)
{ {
const Invoice* currentInvoice = currentInvoices.getValueAt(iterator); const Invoice* currentInvoice = currentInvoices.getValueAt(iterator);
if (currentInvoice && currentInvoice->getStatus() == util::PaymentStatus::PENDING) if (currentInvoice && currentInvoice->getStatus() == requiredStatus)
{ {
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);
} }
@@ -376,6 +443,9 @@ Returns:
inline 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)
@@ -390,8 +460,76 @@ inline 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;
} }
} }
@@ -408,35 +546,53 @@ Throws:
*/ */
inline 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"
@@ -453,13 +609,117 @@ inline 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 || currentJobCard->getStatus() == util::ServiceJobStatus::IN_PROGRESS))
{
startedJobCards.insert(currentJobCard->getId(), currentJobCard);
}
}
return startedJobCards;
}
/*
Function: filterJobCards
Description:
Filters the given list of job cards and returns only those
whose status matches the specified ServiceJobStatus.
Parameters:
- assignedJobCards: util::Map<std::string, const JobCard*>&
Map of job card IDs to JobCard pointers assigned to the technician.
- selectedJobStatus: util::ServiceJobStatus
The status type to filter job cards by.
Returns:
- util::Map<std::string, const JobCard*>
A map containing only job cards with the specified status.
*/
inline util::Map<std::string, const JobCard*> filterJobCards(util::Map<std::string, const JobCard*>& assignedJobCards, util::ServiceJobStatus selectedJobStatus)
{
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() == selectedJobStatus)
{
startedJobCards.insert(currentJobCard->getId(), currentJobCard);
}
}
return startedJobCards;
}
/*
Function: displayAllJobs
Description: Displays all Jobs assigned to a Technician
Parameters:
- assignedJobCards: util::Map<std::string, const JobCard*>&, job cards assigned to the technician
Returns:
- std::string: ID of the selected job card, or empty string if none selected
*/
inline void displayAllJobs(util::Map<std::string, const JobCard*>& assignedJobCards)
{
if (assignedJobCards.getSize() == 0)
{
std::cout << "No active jobs assigned.\n";
return;
}
std::cout << std::endl;
std::cout << std::left
<< std::setw(12) << "BookingID"
<< std::setw(12) << "JobID"
<< std::setw(20) << "ServiceName"
<< std::setw(12) << "ServiceID"
<< std::setw(12) << "Status"
<< std::endl;
for (int iterator = 0; iterator < assignedJobCards.getSize(); iterator++)
{
const JobCard* currentJobCard = assignedJobCards.getValueAt(iterator);
if (currentJobCard && (currentJobCard->getStatus() == util::ServiceJobStatus::STARTED || currentJobCard->getStatus() == util::ServiceJobStatus::IN_PROGRESS))
{
std::cout << std::left << std::setw(12) << currentJobCard->getBookingId()
<< std::setw(12) << currentJobCard->getId()
<< std::setw(20) << util::truncateString(currentJobCard->getService()->getName(), 15)
<< std::setw(12) << currentJobCard->getServiceId()
<< std::setw(12) << util::getServiceJobStatusString(currentJobCard->getStatus())
<< std::endl;
}
} }
} }
@@ -468,43 +728,59 @@ Function: selectJobCardToComplete
Description: Lists all incomplete job cards assigned to the technician and allows selection by index. Description: Lists all incomplete job cards assigned to the technician and allows selection by index.
Parameters: Parameters:
- assignedJobCards: util::Map<std::string, const JobCard*>&, job cards assigned to the technician - assignedJobCards: util::Map<std::string, const JobCard*>&, job cards assigned to the technician
- incompleteJobCards: util::Map<int, const JobCard*>&, map of incomplete job cards indexed for selection
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
*/ */
inline std::string selectJobCardToComplete(util::Map<std::string, const JobCard*>& assignedJobCards, util::Map<int, const JobCard*>& incompleteJobCards) inline std::string selectJobCardToUpdate(util::Map<std::string, const JobCard*>& assignedJobCards, util::ServiceJobStatus selectedJobStatusType)
{ {
util::Map<int, const JobCard* > incompleteJobCards;
if (assignedJobCards.getSize() == 0)
{
std::cout << "\nNo jobs available.\n\n";
return "";
}
int currentIndex = 1; int currentIndex = 1;
int choice; int choice;
bool hasIncompleteJobCard = false; if (selectedJobStatusType == util::ServiceJobStatus::STARTED)
{
util::clear();
std::cout << "Select a job to mark as In Progress\n";
}
else if (selectedJobStatusType == util::ServiceJobStatus::IN_PROGRESS)
{
util::clear();
std::cout << "Select a job to mark as Completed\n";
}
else
{
std::cout << "Unable to update completed or pending jobs.\n\n";
return "";
}
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"
<< std::setw(12) << "JobID" << std::setw(12) << "JobID"
<< std::setw(20) << "ServiceName" << std::setw(20) << "ServiceName"
<< std::setw(12) << "ServiceID" << std::setw(12) << "ServiceID"
<< std::setw(12) << "JobStatus"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < assignedJobCards.getSize(); iterator++) for (int iterator = 0; iterator < assignedJobCards.getSize(); iterator++)
{ {
const JobCard* currentJobCard = assignedJobCards.getValueAt(iterator); const JobCard* currentJobCard = assignedJobCards.getValueAt(iterator);
if (currentJobCard && (currentJobCard->getStatus() == util::ServiceJobStatus::STARTED)) if (currentJobCard && (currentJobCard->getStatus() == selectedJobStatusType))
{ {
std::cout << std::left << std::setw(6) << currentIndex std::cout << std::left << std::setw(6) << currentIndex
<< std::setw(12) << currentJobCard->getBookingId() << std::setw(12) << currentJobCard->getBookingId()
<< std::setw(12) << currentJobCard->getId() << std::setw(12) << currentJobCard->getId()
<< std::setw(20) << currentJobCard->getService()->getName() << std::setw(20) << util::truncateString(currentJobCard->getService()->getName(), 15)
<< std::setw(12) << currentJobCard->getServiceId() << std::setw(12) << currentJobCard->getServiceId()
<< std::setw(12) << util::getServiceJobStatusString(currentJobCard->getStatus())
<< std::endl; << std::endl;
hasIncompleteJobCard = true;
incompleteJobCards.insert(currentIndex++, currentJobCard); incompleteJobCards.insert(currentIndex++, currentJobCard);
} }
} }
if (!hasIncompleteJobCard) std::cout << "Enter the job index to update: ";
{
std::cout << "No pending jobs are present.\n";
return "";
}
std::cout << "Select the Job Card to complete (Index): ";
util::read(choice); util::read(choice);
int selectedJobCardIndex = incompleteJobCards.find(choice); int selectedJobCardIndex = incompleteJobCards.find(choice);
if (selectedJobCardIndex != -1) if (selectedJobCardIndex != -1)
@@ -515,6 +791,7 @@ inline std::string selectJobCardToComplete(util::Map<std::string, const JobCard*
else else
{ {
std::cout << "Invalid index.\n"; std::cout << "Invalid index.\n";
std::cout << "Failed to update job.\n\n";
return ""; return "";
} }
} }
@@ -531,9 +808,9 @@ inline const Notification* selectNotification(const util::Vector<const Notificat
{ {
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;
@@ -543,9 +820,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(35) << 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);
@@ -687,17 +964,18 @@ inline util::Map<std::string, const User*> filterActiveUsers(const util::Map<std
} }
/* /*
Function: displayAllActiveUsers Function: displayAllUsers
Description: Displays all active users in a tabular format with index, ID, username, and type. Description: Displays all active users in a tabular format with index, ID, username, and type.
Parameter: util::Map<std::string, const User*>& activeUsers - active users list Parameter: util::Map<std::string, const User*>& activeUsers - active users list
int activeUserCount - number of active users
Return type: void Return type: void
*/ */
inline void displayAllActiveUsers(util::Map<std::string, const User*>& activeUsers, int activeUserCount) inline void displayAllUsers(util::Map<std::string, const User*>& activeUsers)
{ {
int activeUserCount = activeUsers.getSize();
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++)
@@ -708,6 +986,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;
} }
@@ -720,6 +999,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.
@@ -728,6 +1029,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;
@@ -774,6 +1081,68 @@ 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: displayAllComboPackages
Description: Displays all active combo packages
Parameters:
- currentComboPackages: util::Map<std::string, const ComboPackage*>, available combo packages
Returns:
- void;
*/
inline void displayAllComboPackages(util::Map<std::string, const ComboPackage*> comboPackages)
{
std::cout << std::endl;
if (comboPackages.getSize() == 0)
{
std::cout << "No active combo packages available." << std::endl;
return;
}
std::cout << std::left
<< std::setw(15) << "Combo ID"
<< std::setw(35) << "Combo Name"
<< std::setw(15) << "Estimate Cost"
<< std::endl;
for (int index = 0; index < comboPackages.getSize(); index++)
{
const ComboPackage* currentComboPackage = comboPackages.getValueAt(index);
if (currentComboPackage && currentComboPackage->getState() != util::State::ACTIVE)
{
continue;
}
std::cout << std::left
<< std::setw(15) << currentComboPackage->getId()
<< std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30)
<< std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage)
<< std::endl;
}
}
/* /*
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.
@@ -785,16 +1154,17 @@ 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 ID" << std::setw(15) << "Combo ID"
<< std::setw(15) << "Combo 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++)
{ {
const ComboPackage* currentComboPackage = comboPackages.getValueAt(index); const ComboPackage* currentComboPackage = comboPackages.getValueAt(index);
if (currentComboPackage->getState() != util::State::ACTIVE) if (currentComboPackage && currentComboPackage->getState() != util::State::ACTIVE)
{ {
continue; continue;
} }
@@ -802,7 +1172,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++;
@@ -870,7 +1240,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);
} }
@@ -908,6 +1278,7 @@ inline void displayInventoryWithItems(util::Map<std::string, const InventoryItem
<< std::endl; << std::endl;
} }
} }
std::cout << std::endl;
} }
/* /*
@@ -927,7 +1298,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);
@@ -935,7 +1306,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: ";
@@ -950,13 +1321,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;
} }
} }
@@ -980,14 +1352,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";
} }
@@ -1004,7 +1376,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++)
@@ -1017,7 +1390,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"
@@ -33,10 +34,11 @@ void TechnicianMenu::showMenu()
int choice; int choice;
util::clear(); util::clear();
std::cout << "Technician Menu" std::cout << "Technician Menu"
<< "\n1. Mark Job as Completed" << "\n1. Display My Jobs"
<< "\n2. View Notifications" << "\n2. Update Job Status"
<< "\n3. Change Password" << "\n3. View Notifications"
<< "\n4. Logout" << "\n4. Change Password"
<< "\n5. Logout"
<< "\nEnter a choice: "; << "\nEnter a choice: ";
util::read(choice); util::read(choice);
if (!handleOperation(choice)) if (!handleOperation(choice))
@@ -63,15 +65,18 @@ bool TechnicianMenu::handleOperation(int choice)
switch (choice) switch (choice)
{ {
case 1: case 1:
completeJob(); displayJobs();
break; break;
case 2: case 2:
viewNotifications(); updateJobStatus();
break; break;
case 3: case 3:
changePassword(); viewNotifications();
break; break;
case 4: case 4:
changePassword();
break;
case 5:
logout(); logout();
return false; return false;
default: default:
@@ -82,28 +87,62 @@ bool TechnicianMenu::handleOperation(int choice)
} }
/* /*
Function: completeJob Function: displayJobs
Description: Allows the technician to mark a selected job card as completed. Description: Displays all Jobs assigned to a Technician
Parameters:
- None
Returns:
- void
*/
void TechnicianMenu::displayJobs()
{
util::clear();
std::cout << "My Jobs\n";
util::Map<std::string, const JobCard*> assignedJobCards = m_controller.getJobCardsByUser();
util::Map<std::string, const JobCard*> jobCards = filterStartedJobCards(assignedJobCards);
displayAllJobs(jobCards);
util::pressEnter();
}
/*
Function: updateJobStatus
Description: Allows the technician to update a selected job card.
Validates selection and updates job status through the controller. Validates selection and updates job status through the controller.
Parameters: Parameters:
- None - None
Returns: Returns:
- void - void
*/ */
void TechnicianMenu::completeJob() void TechnicianMenu::updateJobStatus()
{ {
util::clear();
std::cout << "Update Job Status\n";
int choice;
std::string selectedJobID;
util::ServiceJobStatus selectedJobStatus = util::ServiceJobStatus::PENDING;
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; std::cout << "Select the type of job you want to update:\n1.Started\n2.In Progress\nChoice: ";
std::cout << "Jobs to be completed.\n"; util::read(choice);
std::string selectedJobID = selectJobCardToComplete(assignedJobCards, incompleteJobCards); if (choice == 1)
if (selectedJobID == "")
{ {
std::cout << "Failed to complete the job.\n"; selectedJobStatus = util::ServiceJobStatus::STARTED;
}
else if (choice == 2)
{
selectedJobStatus = util::ServiceJobStatus::IN_PROGRESS;
} }
else else
{ {
m_controller.completeJob(selectedJobID); std::cout << "Invalid choice. Please try again.\n";
std::cout << "Job marked as completed.\n"; util::pressEnter();
return;
}
util::Map<std::string, const JobCard*> selectedTypeJobCard = filterJobCards(assignedJobCards, selectedJobStatus);
selectedJobID = selectJobCardToUpdate(selectedTypeJobCard, selectedJobStatus);
if (!selectedJobID.empty())
{
m_controller.updateJobStatus(selectedJobID);
std::cout << "\nJob status updated.\n\n";
} }
util::pressEnter(); util::pressEnter();
} }
@@ -17,7 +17,8 @@ private:
bool handleOperation(int choice); bool handleOperation(int choice);
public: public:
void showMenu(); void showMenu();
void completeJob(); void displayJobs();
void updateJobStatus();
void viewNotifications(); void viewNotifications();
void logout(); void logout();
void changePassword(); void changePassword();
@@ -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