Compare commits

...

8 Commits

Author SHA1 Message Date
Avinash Rajesh c64f3cff72 Implemented Review Fixes 2026-06-01 18:13:11 +05:30
Avinash Rajesh 20475ace73 Fix Duplicate Customer Notification Sent When Assigned Technician Is Removed
- Refactored processBookingCancellation to simplify parameters and remove redundant notification arguments.
- Added util::UserType parameter to differentiate cancellation flows for CUSTOMER vs TECHNICIAN.
- Updated cancelCustomerServiceBookings to use processBookingCancellation with util::UserType::CUSTOMER.
- Updated cancelTechnicianJobs to use processBookingCancellation with util::UserType::TECHNICIAN.
- Enhanced booking status handling by including IN_PROGRESS status in cancellation checks.
- Ensured job cards are consistently marked CANCELLED and inventory restored.
- Fixed duplicate notification issue where customers received multiple alerts when technician was removed.

Fixes #1807
2026-06-01 17:46:55 +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
14 changed files with 332 additions and 113 deletions
@@ -392,9 +392,9 @@ Parameters:
Returns:
- void
*/
void Controller::completeJob(const std::string& jobID)
void Controller::updateJobStatus(const std::string& jobID)
{
m_serviceManagementService.completeJob(jobID);
m_serviceManagementService.updateJobStatus(jobID);
}
/*
@@ -462,6 +462,38 @@ util::Map<std::string, const Invoice*> Controller::getInvoicesByUser()
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
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 removeService(const std::string& serviceID);
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 createComboPackage(const std::string& name, const util::Vector<std::string>& serviceIDs, double discountPercentage);
void removeComboPackage(const std::string& comboPackageID);
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);
util::Vector<const Notification*> getNotifications();
void deleteNotification(const std::string& notificationID);
@@ -368,12 +368,12 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti
if (invoiceIndex != -1)
{
Invoice* invoice = currentInvoices.getValueAt(invoiceIndex);
if (invoice && invoice->getStatus() != util::PaymentStatus::COMPLETED)
if (invoice && invoice->getStatus() != util::PaymentStatus::PAID)
{
User* currentUser = invoice->getBooking()->getCustomer();
invoice->setPaymentMethod(paymentMode);
invoice->setPaymentDate(util::Timestamp());
invoice->setStatus(util::PaymentStatus::COMPLETED);
invoice->setStatus(util::PaymentStatus::PAID);
std::string title, message;
title = "Payment successful";
message = "Payment successful for Invoice ID " + invoiceID;
@@ -384,4 +384,48 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti
{
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);
util::Map<std::string, Invoice*> getInvoices(const std::string& customerID);
void completePayment(const std::string& invoiceID, util::PaymentMode paymentMode);
util::Map<std::string, Invoice*>& getAllInvoices();
void confirmPayment(const std::string& invoiceID);
void sendPaymentReminders();
void sendNotification(User* user, const std::string& title, const std::string& message) override;
void attach(User* user) override;
@@ -543,50 +543,65 @@ static void restoreInventory(ServiceBooking* booking)
/*
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
Description: Handles cancellation or reassignment of a service booking based on user type.
Cancels associated job cards, updates booking status, clears technician assignments,
restores inventory, and sends appropriate notifications.
Parameters:
ServiceBooking* booking - The booking to cancel or reset
util::Map<std::string, JobCard*>& jobs - Collection of job cards to update
ServiceManagementService& currentService - Service layer for notifications
util::UserType userType - Type of user initiating cancellation (CUSTOMER or TECHNICIAN)
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)
util::Map<std::string, JobCard*>& jobs,
ServiceManagementService& currentService,
util::UserType userType)
{
if (!booking || !notifyUser)
if (!booking)
{
return;
}
for (int jobIterator = 0; jobIterator < jobs.getSize(); ++jobIterator)
{
JobCard* jobCard = jobs.getValueAt(jobIterator);
if (jobCard && jobCard->getBookingId() == booking->getId())
if (!jobCard || jobCard->getBookingId() != booking->getId() || jobCard->getStatus() == util::ServiceJobStatus::CANCELLED)
{
jobCard->setStatus(jobCardStatus);
currentService.sendNotification(notifyUser, jobNotificationTitle, jobNotificationMessage);
continue;
}
jobCard->setStatus(util::ServiceJobStatus::CANCELLED);
if (userType == util::UserType::CUSTOMER)
{
if (User* technician = booking->getAssignedTechnician())
{
const std::string jobTitle = "Job Cancelled";
const std::string jobMessage = "Your job card has been cancelled and the inventory has been restocked.";
currentService.sendNotification(technician, jobTitle, jobMessage);
}
}
}
booking->setStatus(newServiceBookingStatus);
currentService.sendNotification(notifyUser, notificationTitle, notificationMessage);
if (newServiceBookingStatus == util::ServiceJobStatus::PENDING)
if (userType == util::UserType::CUSTOMER)
{
booking->setAssignedTechnician(nullptr);
booking->setAssignedTechnicianId("");
booking->setStatus(util::ServiceJobStatus::CANCELLED);
if (User* technician = booking->getAssignedTechnician())
{
const std::string title = "Customer Service Cancelled";
const std::string message = "Your assigned job card has been cancelled and the inventory has been restocked.";
currentService.sendNotification(technician, title, message);
}
}
else if (userType == util::UserType::TECHNICIAN)
{
booking->setStatus(util::ServiceJobStatus::PENDING);
if (User* customer = booking->getCustomer())
{
const std::string title = "Technician Unavailable";
const std::string message = "Your booking has been reset to pending and we will reassign a new technician shortly.";
currentService.sendNotification(customer, title, message);
}
}
booking->setAssignedTechnician(nullptr);
booking->setAssignedTechnicianId("");
restoreInventory(booking);
}
@@ -624,21 +639,13 @@ void ServiceManagementService::cancelCustomerServiceBookings(const std::string&
{
continue;
}
if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED)
if (booking->getStatus() != util::ServiceJobStatus::PENDING &&
booking->getStatus() != util::ServiceJobStatus::STARTED &&
booking->getStatus() != util::ServiceJobStatus::IN_PROGRESS)
{
continue;
}
User* assignedTechnician = booking->getAssignedTechnician();
std::string titleToTechnician = "Customer Service Cancelled";
std::string messageToTechnician = "The customer has cancelled their service booking. Your assigned job card has been cancelled and the inventory has been restocked.";
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.";
processBookingCancellation(booking,
util::ServiceJobStatus::CANCELLED,
titleToTechnician, messageToTechnician, assignedTechnician,
util::ServiceJobStatus::CANCELLED,
jobTitle, jobMessage, jobs, *this
);
processBookingCancellation(booking, jobs, *this, util::UserType::CUSTOMER);
}
}
@@ -676,7 +683,9 @@ void ServiceManagementService::cancelTechnicianJobs(const std::string& technicia
{
continue;
}
if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED)
if (booking->getStatus() != util::ServiceJobStatus::PENDING &&
booking->getStatus() != util::ServiceJobStatus::STARTED &&
booking->getStatus() != util::ServiceJobStatus::IN_PROGRESS)
{
continue;
}
@@ -685,14 +694,7 @@ void ServiceManagementService::cancelTechnicianJobs(const std::string& technicia
{
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
);
processBookingCancellation(booking, jobs, *this, util::UserType::TECHNICIAN);
}
}
@@ -1080,7 +1082,7 @@ static bool hasCompletedAllJobs(std::string bookingId, util::Map<std::string, Jo
JobCard* currentJob = currentAssignedJobs.getValueAt(iterator);
if (currentJob->getBookingId() == bookingId)
{
if (currentJob->getStatus() == util::ServiceJobStatus::STARTED)
if (currentJob->getStatus() != util::ServiceJobStatus::COMPLETED && currentJob->getStatus() != util::ServiceJobStatus::CANCELLED)
{
return false;
}
@@ -1090,18 +1092,19 @@ static bool hasCompletedAllJobs(std::string bookingId, util::Map<std::string, Jo
}
/*
Function: completeJob
Description: Marks a job card as completed for the authenticated technician.
If all job cards in the booking are completed, marks the booking as completed
and generates an invoice.
Function: updateJobStatus
Description:
Updates the status of a job card assigned to the currently authenticated technician.
- 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:
- jobID: std::string, ID of the job card
- jobID: const std::string&, unique identifier of the job card to update.
Returns:
- void
Throws:
- std::runtime_error if technician is not authenticated, job card not found, or job already completed
- void
*/
void ServiceManagementService::completeJob(const std::string& jobID)
void ServiceManagementService::updateJobStatus(const std::string& jobID)
{
AuthenticationManagementService authenticationManagementService;
PaymentManagementService paymentManagementService;
@@ -1126,26 +1129,30 @@ void ServiceManagementService::completeJob(const std::string& jobID)
}
if (currentJob->getStatus() == util::ServiceJobStatus::STARTED)
{
currentJob->setStatus(util::ServiceJobStatus::COMPLETED);
currentJob->setStatus(util::ServiceJobStatus::IN_PROGRESS);
jobStatusUpdated = true;
}
else if (currentJob->getStatus() == util::ServiceJobStatus::IN_PROGRESS)
{
currentJob->setStatus(util::ServiceJobStatus::COMPLETED);
jobStatusUpdated = true;
serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs);
if (serviceBookingCompleted)
{
currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED);
paymentManagementService.generateInvoice(currentJob->getBooking());
std::string title = "Service Booking completed. Invoice Generated.";
std::string message = "Services completed for the booking and invoice generated.";
sendNotification(currentJob->getBooking()->getCustomer(), title, message);
}
}
}
else
{
throw std::runtime_error("Failed to complete the job, some error occurred or job already completed.");
throw std::runtime_error("Failed to update job status. Job may already be completed.");
}
if (!jobStatusUpdated)
{
throw std::runtime_error("Failed to complete the job, some error occurred or job already completed.");
}
serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs);
if (serviceBookingCompleted)
{
currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED);
paymentManagementService.generateInvoice(currentJob->getBooking());
std::string title = "Service Booking completed. Invoice Generated.";
std::string message = "Services completed for the booking and invoice generated.";
sendNotification(currentJob->getBooking()->getCustomer(), title, message);
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 removeService(const std::string& serviceID);
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 cancelTechnicianJobs(const std::string& technicianID);
void createComboPackage(const std::string& packageName, const util::Vector<std::string>& serviceIDs, double discountPercentage);
@@ -29,7 +29,8 @@ namespace util
enum class PaymentStatus
{
PENDING,
COMPLETED
COMPLETED,
PAID
};
enum class ServiceJobStatus
@@ -37,6 +38,7 @@ namespace util
PENDING,
STARTED,
COMPLETED,
IN_PROGRESS,
CANCELLED
};
@@ -160,6 +162,8 @@ namespace util
return "PENDING";
case PaymentStatus::COMPLETED:
return "COMPLETED";
case PaymentStatus::PAID:
return "PAID";
}
throw std::invalid_argument("Invalid PaymentStatus");
}
@@ -209,6 +213,8 @@ namespace util
return "COMPLETED";
case ServiceJobStatus::CANCELLED:
return "CANCELLED";
case ServiceJobStatus::IN_PROGRESS:
return "IN_PROGRESS";
}
throw std::invalid_argument("Invalid ServiceJobStatus");
}
@@ -241,6 +247,10 @@ namespace util
{
return ServiceJobStatus::CANCELLED;
}
if (value == "IN_PROGRESS")
{
return ServiceJobStatus::IN_PROGRESS;
}
throw std::invalid_argument("Invalid ServiceJobStatus string");
}
@@ -53,7 +53,8 @@ void AdminMenu::showMenu()
<< "\n14. Remove Combo Package"
<< "\n15. View Notifications"
<< "\n16. Change Password"
<< "\n17. Logout"
<< "\n17. Confirm Payment"
<< "\n18. Logout"
<< "\nEnter a choice: ";
util::read(choice);
if (!handleOperation(choice))
@@ -127,7 +128,10 @@ bool AdminMenu::handleOperation(int choice)
case 16:
changePassword();
break;
case 17:
case 17:
confirmPayment();
break;
case 18:
logout();
return false;
default:
@@ -496,6 +500,54 @@ void AdminMenu::displayUsers()
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
Description: Adds a new technician after validating username, password, email, and phone number.
@@ -28,6 +28,7 @@ public:
void createService();
void removeService();
void displayUsers();
void confirmPayment();
void addTechnician();
void removeUser();
void displayComboPackages();
@@ -338,7 +338,7 @@ void CustomerMenu::completePayments()
util::pressEnter();
return;
}
std::string selectedID = selectInvoiceFromUserForPayment(currentInvoices);
std::string selectedID = selectInvoiceFromUserForPayment(currentInvoices, util::PaymentStatus::PENDING);
if (selectedID == "")
{
std::cout << "Payment failed.\n";
@@ -364,14 +364,18 @@ inline const User* selectTechnician(util::Map<int, const User*>& currentAvailabl
}
/*
Function: selectInvoiceFromUserForPayment
Description: Lists all pending invoices for the customer and allows selection by index.
Function: selectInvoiceFromUserForPayment
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:
- 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:
- 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)
{
int currentIndex = 1, choice;
util::Map<int, const Invoice*> pendingInvoicesForPayment;
@@ -389,7 +393,7 @@ inline std::string selectInvoiceFromUserForPayment(const util::Map<std::string,
for (int iterator = 0; iterator < currentInvoices.getSize(); 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();
std::cout << std::left
@@ -627,7 +631,6 @@ inline void displayInvoices(util::Map<std::string, const Invoice*> currentUserIn
std::cout << "Unable to fetch the selected invoice\n";
doRun = false;
}
} while (doRun);
}
}
@@ -646,7 +649,35 @@ inline util::Map<std::string, const JobCard*> filterStartedJobCards(util::Map<st
for (int iterator = 0; iterator < assignedJobCards.getSize(); iterator++)
{
const JobCard* currentJobCard = assignedJobCards.getValueAt(iterator);
if (currentJobCard && currentJobCard->getStatus() == util::ServiceJobStatus::STARTED)
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);
}
@@ -675,16 +706,18 @@ inline void displayAllJobs(util::Map<std::string, const JobCard*>& assignedJobCa
<< 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))
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) << currentJobCard->getService()->getName()
<< std::setw(20) << util::truncateString(currentJobCard->getService()->getName(), 15)
<< std::setw(12) << currentJobCard->getServiceId()
<< std::setw(12) << util::getServiceJobStatusString(currentJobCard->getStatus())
<< std::endl;
}
}
@@ -698,16 +731,31 @@ Parameters:
Returns:
- 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)
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 << "No started jobs available to complete.\n";
std::cout << "No jobs available.\n\n";
return "";
}
int currentIndex = 1;
int choice;
if (selectedJobStatusType == util::ServiceJobStatus::STARTED)
{
util::clear();
std::cout << "Select a job to update to Inprogress\n";
}
else if (selectedJobStatusType == util::ServiceJobStatus::IN_PROGRESS)
{
util::clear();
std::cout << "Select a job to update to Completed\n";
}
else
{
std::cout << "Unable to update completed or pending jobs.\n\n";
return "";
}
std::cout << std::endl;
std::cout << std::left
<< std::setw(6) << "Index"
@@ -715,22 +763,24 @@ inline std::string selectJobCardToComplete(util::Map<std::string, const JobCard*
<< std::setw(12) << "JobID"
<< std::setw(20) << "ServiceName"
<< std::setw(12) << "ServiceID"
<< std::setw(12) << "JobStatus"
<< std::endl;
for (int iterator = 0; iterator < assignedJobCards.getSize(); 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::setw(12) << currentJobCard->getBookingId()
<< 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) << util::getServiceJobStatusString(currentJobCard->getStatus())
<< std::endl;
incompleteJobCards.insert(currentIndex++, currentJobCard);
}
}
std::cout << "Select the Job Card to complete (Index): ";
std::cout << "Select the Job Card to Update (Index): ";
util::read(choice);
int selectedJobCardIndex = incompleteJobCards.find(choice);
if (selectedJobCardIndex != -1)
@@ -741,7 +791,7 @@ inline std::string selectJobCardToComplete(util::Map<std::string, const JobCard*
else
{
std::cout << "Invalid index.\n";
std::cout << "Failed to complete jobs.\n\n";
std::cout << "Failed to update job.\n\n";
return "";
}
}
@@ -35,7 +35,7 @@ void TechnicianMenu::showMenu()
util::clear();
std::cout << "Technician Menu"
<< "\n1. Display My Jobs"
<< "\n2. Mark Job as Completed"
<< "\n2. Update Job Status"
<< "\n3. View Notifications"
<< "\n4. Change Password"
<< "\n5. Logout"
@@ -68,7 +68,7 @@ bool TechnicianMenu::handleOperation(int choice)
displayJobs();
break;
case 2:
completeJob();
updateJobStatus();
break;
case 3:
viewNotifications();
@@ -99,31 +99,50 @@ 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*> startedJobCards = filterStartedJobCards(assignedJobCards);
displayAllJobs(startedJobCards);
util::Map<std::string, const JobCard*> jobCards = filterStartedJobCards(assignedJobCards);
displayAllJobs(jobCards);
util::pressEnter();
}
/*
Function: completeJob
Description: Allows the technician to mark a selected job card as completed.
Function: updateJobStatus
Description: Allows the technician to update a selected job card.
Validates selection and updates job status through the controller.
Parameters:
- None
Returns:
- void
*/
void TechnicianMenu::completeJob()
void TechnicianMenu::updateJobStatus()
{
util::clear();
std::cout << "Complete Job\n";
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*> startedJobCards = filterStartedJobCards(assignedJobCards);
std::string selectedJobID = selectJobCardToComplete(startedJobCards);
std::cout << "Select the type of job you want to update\n1.Started\n2.Inprogress\nChoice: ";
util::read(choice);
if (choice == 1)
{
selectedJobStatus = util::ServiceJobStatus::STARTED;
}
else if (choice == 2)
{
selectedJobStatus = util::ServiceJobStatus::IN_PROGRESS;
}
else
{
std::cout << "Invalid choice. Please try again.\n";
util::pressEnter();
return;
}
util::Map<std::string, const JobCard*> selectedTypeJobCard = filterJobCards(assignedJobCards, selectedJobStatus);
selectedJobID = selectJobCardToUpdate(selectedTypeJobCard, selectedJobStatus);
if (!selectedJobID.empty())
{
m_controller.completeJob(selectedJobID);
std::cout << "\nJob marked as completed.\n\n";
m_controller.updateJobStatus(selectedJobID);
std::cout << "\nJob status updated.\n\n";
}
util::pressEnter();
}
@@ -18,7 +18,7 @@ private:
public:
void showMenu();
void displayJobs();
void completeJob();
void updateJobStatus();
void viewNotifications();
void logout();
void changePassword();
@@ -27,4 +27,4 @@ public:
void run();
void login();
void registerCustomer();
};
};