Compare commits

...

3 Commits

Author SHA1 Message Date
Jissin Mathew 684d6d3860 Implement review fixes 2026-06-18 19:43:41 +05:30
Jissin Mathew 2eaa719aca Fix Multiple Notifications Contain Formatting Issues, Vague Messaging, and Duplicate Entries
Changes:

- Updated ServiceManagementService::createJobCard() to prevent duplicate
  "Technician assigned" notifications when multiple job cards exist for
  the same booking.
- Refined job card creation notification to include Job Card ID, Service
  ID, and Booking ID for clearer technician communication.
- Corrected service booking cancellation notification formatting by
  standardizing capitalization and improving message clarity.
- Improved service booking completion notification to explicitly mention
  the booking ID and confirm invoice generation.
- Ensured consistent notification titles and messages across the
  workflow for better user understanding.

Fixes #2114
2026-06-18 19:21:59 +05:30
Jissin Mathew b983337630 Merged PR 1196: Fix Service Booking Cancellation, Job Completion Exception, and Inventory Quantity Handling
Changes

- Added Controller::removeServiceBooking() with documentation and delegation to ServiceManagementService.

- Implemented ServiceManagementService::removeServiceBooking() to handle cancellation of pending bookings, enforce status validation, send notifications, and persist changes safely.

- Updated CustomerMenu to include a "Cancel Service Booking" option, wired into handleOperation, and implemented CustomerMenu::cancelServiceBooking() for user interaction.

- Ensured consistent declarations in Controller.h, ServiceManagementService.h, and CustomerMenu.h.

- Refactored ServiceManagementService::updateJobStatus() to use tracked job card references instead of raw pointers, ensuring consistent state updates.

- Added proper null checks and error handling for current job retrieval to prevent unexpected termination.

- Updated logic to mark tracked job records as MODIFIED when status transitions occur (`STARTED` → `IN_PROGRESS`, `IN_PROGRESS` → `COMPLETED`).

- Simplified control flow and indentation for better readability and maintainability.

- Added retrieval of tracked service bookings and inventory items in ServiceManagementService::createJobCard().

- Validated service booking ID and inventory item indices before proceeding with job card creation.

- Decremented inventory item quantities when job cards are created and marked corresponding tracked inventory records as `MODIFIED`.

- Updated tracked service booking state to `MODIFIED` when technician is assigned and job status changes.

- Persisted changes by saving job cards, service bookings, and inventory items to the datastore.

Fixes

#2105
#2076
#2075
#2074
2026-06-18 11:36:43 +05:30
@@ -629,6 +629,7 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const
DataStoreLockGuard lock(m_dataStore);
UserManagementService m_userManagementService;
ServiceBooking* currentBooking = getServiceBooking(bookingID);
std::string title, message;
if (currentBooking == nullptr)
{
throw std::runtime_error("Service Booking not available");
@@ -684,18 +685,30 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const
trackedCurrentInventoryItem.state = RecordState::MODIFIED;
}
}
const User* currentAssignedTechnician = currentBooking->getAssignedTechnician();
const std::string& currentAssignedTechnicianId = currentBooking->getAssignedTechnicianId();
if (!currentAssignedTechnician && currentAssignedTechnicianId.empty())
{
currentBooking->setAssignedTechnician(selectedTechnician);
currentBooking->setAssignedTechnicianId(selectedTechnician->getId());
title = "Technician assigned";
message = "A technician has been assigned to your Service Booking with ID " + bookingID;
sendNotification(currentBooking->getCustomer(), title, message);
}
if (currentBooking->getStatus() == util::ServiceJobStatus::PENDING)
{
currentBooking->setStatus(util::ServiceJobStatus::STARTED);
}
currentTrackedServiceBooking.state = RecordState::MODIFIED;
std::string title = "Job card created";
std::string message = "Job card created for the service and you are assigned for that.";
JobCard* jobCard = Factory::getObject<JobCard>(bookingID, currentBooking, currentService, serviceID, technicianID, selectedTechnician, util::Timestamp(), util::ServiceJobStatus::STARTED, util::Timestamp());
if (jobCard)
{
title = "Job Card Assigned";
message = "A new Job Card (ID: " + jobCard->getId() +
") has been created for Service " + serviceID +
" in Booking " + bookingID +
". You have been assigned to this job.";
currentTrackedJobCards.insert(jobCard->getId(), util::createNewRecord(jobCard));
sendNotification(selectedTechnician, title, message);
}
@@ -703,9 +716,6 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const
{
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);
m_dataStore.saveJobCards();
m_dataStore.saveServiceBookings();
m_dataStore.saveInventoryItems();
@@ -852,8 +862,8 @@ void ServiceManagementService::removeServiceBooking(const std::string& bookingID
{
if (currentServiceBooking->getStatus() == util::ServiceJobStatus::PENDING)
{
const std::string title = "Service Booking cancelled.";
const std::string message = "Service Booking of id " + bookingID + " successfully cancelled.";
const std::string title = "Service Booking Cancelled";
const std::string message = "Service Booking (ID: " + bookingID + ") has been successfully cancelled";
currentServiceBooking->setStatus(util::ServiceJobStatus::CANCELLED);
currentTrackedServiceBooking.state = RecordState::MODIFIED;
serviceBookingRemoved = true;
@@ -1025,8 +1035,8 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID)
currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED);
trackedServiceBookings.getValueAt(trackedServiceBookings.find(bookingId)).state = RecordState::MODIFIED;
paymentManagementService.generateInvoice(currentJob->getBooking());
std::string title = "Service Booking completed. Invoice Generated.";
std::string message = "Services completed for the booking and invoice generated.";
std::string title = "Service Booking Completed";
std::string message = "Service Booking (ID: " + bookingId + ") has been completed successfully. An invoice has been generated.";
sendNotification(currentJob->getBooking()->getCustomer(), title, message);
}
}