Compare commits

..

2 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
4 changed files with 48 additions and 42 deletions
@@ -358,7 +358,7 @@ util::Map<std::string, TrackedRecord<ServiceBooking>>& DataStore::getServiceBook
{
throw std::runtime_error("Invalid service index.");
}
auto& currentService = services.getValueAt(serviceIndex);
auto currentService = services.getValueAt(serviceIndex);
servicesInBooking[currentServiceId] = currentService.data;
}
serviceBooking->setServices(servicesInBooking);
@@ -369,7 +369,7 @@ util::Map<std::string, TrackedRecord<ServiceBooking>>& DataStore::getServiceBook
{
throw std::runtime_error("Invalid user index.");
}
auto& customer = users.getValueAt(userIndex);
auto customer = users.getValueAt(userIndex);
serviceBooking->setCustomer(customer.data);
}
if (!serviceBooking->getAssignedTechnicianId().empty())
@@ -379,7 +379,7 @@ util::Map<std::string, TrackedRecord<ServiceBooking>>& DataStore::getServiceBook
{
throw std::runtime_error("Invalid technician index.");
}
auto& technician = users.getValueAt(technicianIndex);
auto technician = users.getValueAt(technicianIndex);
serviceBooking->setAssignedTechnician(technician.data);
}
}
@@ -423,7 +423,7 @@ util::Map<std::string, TrackedRecord<JobCard>>& DataStore::getJobCards()
{
throw std::runtime_error("Invalid service ID: " + serviceId);
}
auto& trackedService = services.getValueAt(serviceIndex);
auto trackedService = services.getValueAt(serviceIndex);
jobCard->setService(trackedService.data);
const std::string& technicianId = jobCard->getTechnicianId();
if (!technicianId.empty())
@@ -433,7 +433,7 @@ util::Map<std::string, TrackedRecord<JobCard>>& DataStore::getJobCards()
{
throw std::runtime_error("Invalid technician ID: " + technicianId);
}
auto& trackedTechnician = users.getValueAt(technicianIndex);
auto trackedTechnician = users.getValueAt(technicianIndex);
jobCard->setTechnician(trackedTechnician.data);
}
}
@@ -691,7 +691,7 @@ void DataStore::saveObservers(MappingInfo& mapping, util::Map<std::string, User*
SharedMemory::setRecordCount(mapping, observerCount);
for (size_t index = 0; index < observerCount; index++)
{
SerializedObserver serializedObserver{};
SerializedObserver serializedObserver;
User* user = observers.getValueAt(static_cast<int>(index));
strcpy_s(serializedObserver.id, sizeof(serializedObserver.id), user->getId().c_str());
SerializedObserver* destination = static_cast<SerializedObserver*>(SharedMemory::getRecordAddress(mapping, index));
@@ -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;
}
}
currentBooking->setAssignedTechnician(selectedTechnician);
currentBooking->setAssignedTechnicianId(selectedTechnician->getId());
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);
}
}
@@ -67,7 +67,7 @@ Return type: void
*/
void Menu::eventListenerLoop()
{
HANDLE handles[3] = { NULL, NULL, NULL };
HANDLE handles[3];
handles[0] = m_accountDisabledEvent;
handles[1] = m_notificationAvailableEvent;
handles[2] = m_shutdownEvent;
@@ -588,7 +588,7 @@ inline void displayInvoices(util::Map<std::string, const Invoice*> currentUserIn
<< 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();
auto inventoryItemsInInvoice = selectedInvoice->getParts();
if (inventoryItemsInInvoice.isEmpty())
{
std::cout << "No inventory items used.\n\n";
@@ -597,6 +597,7 @@ inline void displayInvoices(util::Map<std::string, const Invoice*> currentUserIn
std::cout << "\nItems Used:\n";
std::cout << std::left
<< std::setw(20) << "ItemName"
<< std::setw(10) << "Quantity"
<< std::setw(10) << "Price"
<< std::endl;
std::cout << std::string(40, '-') << std::endl;
@@ -605,6 +606,7 @@ inline void displayInvoices(util::Map<std::string, const Invoice*> currentUserIn
InventoryItem* currentItem = inventoryItemsInInvoice.getValueAt(iterator);
std::cout << std::left
<< std::setw(20) << currentItem->getPartName()
<< std::setw(10) << currentItem->getQuantity()
<< std::setw(10) << currentItem->getPrice()
<< std::endl;
}
@@ -1144,18 +1146,15 @@ inline void displayAllComboPackages(util::Map<std::string, const ComboPackage*>
for (int index = 0; index < comboPackages.getSize(); index++)
{
const ComboPackage* currentComboPackage = comboPackages.getValueAt(index);
if (currentComboPackage)
if (currentComboPackage && currentComboPackage->getState() != util::State::ACTIVE)
{
if (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;
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;
}
}
@@ -1181,21 +1180,18 @@ inline const ComboPackage* selectComboPackageFromPackages(const util::Map<std::s
for (int index = 0; index < comboPackages.getSize(); index++)
{
const ComboPackage* currentComboPackage = comboPackages.getValueAt(index);
if (currentComboPackage)
if (currentComboPackage && currentComboPackage->getState() != util::State::ACTIVE)
{
if (currentComboPackage->getState() != util::State::ACTIVE)
{
continue;
}
activeComboPackages.insert(currentIndex, currentComboPackage);
std::cout << std::left
<< std::setw(10) << currentIndex
<< std::setw(15) << currentComboPackage->getId()
<< std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30)
<< std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage)
<< std::endl;
currentIndex++;
continue;
}
activeComboPackages.insert(currentIndex, currentComboPackage);
std::cout << std::left
<< std::setw(10) << currentIndex
<< std::setw(15) << currentComboPackage->getId()
<< std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30)
<< std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage)
<< std::endl;
currentIndex++;
}
if (activeComboPackages.getSize() == 0)
{