Merged PR 1170: Inventory Management Refactoring - 1926

User Story #1957
User Story #1958
**Changes**
- Added DataStoreLockGuard for scoped datastore locking/unlocking.
- Refactored InventoryItem serialization from CSV strings to SerializedInventoryItem struct.
- Removed old serialize, deserialize, and getHeaders methods from InventoryItem.
- Updated DataStore to load, refresh, and save inventory items using typed records.
- Applied lock guard in InventoryManagementService for stock alerts, add/remove/update operations.
- Enhanced error handling for invalid item IDs and missing inventory records.
- Simplified getInventoryItems to return object maps from tracked records.
- Removed redundant persistence methods (loadInventoryItems, saveInventoryItems) from service layer.

Related work items: #1926, #1957, #1958
This commit is contained in:
Avinash Rajesh
2026-06-15 15:26:00 +05:30
committed by Joel Thomas
4 changed files with 88 additions and 92 deletions
@@ -324,6 +324,8 @@ Returns:
*/
util::Map<std::string, TrackedRecord<InventoryItem>>& DataStore::getInventoryItems()
{
auto inventoryItems = loadRecords<InventoryItem, SerializedInventoryItem>(m_inventoryItems);
refreshCache(m_inventoryItemCache, inventoryItems);
return m_inventoryItemCache;
}
@@ -583,6 +585,7 @@ Returns:
*/
void DataStore::saveInventoryItems()
{
saveRecords<InventoryItem, SerializedInventoryItem>(m_inventoryItems, m_inventoryItemCache);
}
/*
@@ -8,6 +8,7 @@ Date: 19-May-2026
#include <sstream>
#include <stdexcept>
#include "SerializedRecords.h"
#include "Factory.h"
#include "StringHelper.h"
#include "InventoryItem.h"
@@ -27,7 +28,8 @@ InventoryItem::InventoryItem()
: m_id("IIM" + std::to_string(++m_uid)),
m_quantity(0),
m_status(util::State::ACTIVE),
m_price(0.0) {}
m_price(0.0) {
}
/*
Function: InventoryItem
@@ -45,7 +47,8 @@ InventoryItem::InventoryItem(const std::string& partName, int quantity, double p
m_partName(partName),
m_quantity(quantity),
m_status(util::State::ACTIVE),
m_price(price) {}
m_price(price) {
}
/*
Function: InventoryItem (parameterized constructor with ID)
@@ -206,73 +209,37 @@ void InventoryItem::setState(util::State status)
/*
Function: serialize
Description: Serializes the inventory item into a CSV-formatted string.
Description: Serializes the InventoryItem object into a SerializedInventoryItem record.
Parameters:
- None
Returns:
- std::string: Serialized inventory item record
- SerializedInventoryItem: Serialized representation of the inventory item
*/
std::string InventoryItem::serialize() const
SerializedInventoryItem InventoryItem::serialize() const
{
std::ostringstream serializedInventoryItem;
serializedInventoryItem << m_id << ','
<< m_partName << ','
<< m_quantity << ','
<< m_price << ','
<< util::getStateString(m_status);
return serializedInventoryItem.str();
SerializedInventoryItem serialized = {};
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str());
strcpy_s(serialized.partName, sizeof(serialized.partName), m_partName.c_str());
serialized.quantity = m_quantity;
serialized.price = m_price;
serialized.status = m_status;
return serialized;
}
/*
Function: deserialize
Description: Deserializes a CSV-formatted string into an InventoryItem object.
Description: Deserializes a SerializedInventoryItem record into an InventoryItem object.
Parameters:
- record: const std::string&, serialized inventory item record
- serializedInventoryItem: const SerializedInventoryItem&, serialized inventory item record
Returns:
- InventoryItem*: Pointer to the deserialized InventoryItem object
Throws:
- std::runtime_error if data is invalid
*/
InventoryItem* InventoryItem::deserialize(const std::string& record)
InventoryItem* InventoryItem::deserialize(const SerializedInventoryItem& serializedInventoryItem)
{
std::string id, partName;
std::string quantityString, priceString, statusString;
int quantity;
double price;
std::istringstream serializedInventoryItem(record);
getline(serializedInventoryItem, id, ',');
getline(serializedInventoryItem, partName, ',');
getline(serializedInventoryItem, quantityString, ',');
getline(serializedInventoryItem, priceString, ',');
getline(serializedInventoryItem, statusString, ',');
try
{
quantity = std::stoi(quantityString);
price = std::stod(priceString);
}
catch (...)
{
throw std::runtime_error("Invalid inventory item data");
}
util::State status = util::getState(statusString);
return Factory::getObject<InventoryItem>(
id,
partName,
quantity,
price,
status
);
}
/*
Function: getHeaders
Description: Retrieves the CSV headers for inventory item serialization.
Parameters:
- None
Returns:
- std::string: Header string ("ID,PartName,Quantity,Price,Status")
*/
std::string InventoryItem::getHeaders()
{
return "ID,PartName,Quantity,Price,Status";
serializedInventoryItem.id,
serializedInventoryItem.partName,
serializedInventoryItem.quantity,
serializedInventoryItem.price,
serializedInventoryItem.status);
}
@@ -6,11 +6,12 @@ Author: Trenser
Date: 19-May-2026
*/
#pragma once
#include <string>
#include "Enums.h"
struct SerializedInventoryItem;
class InventoryItem
{
private:
@@ -34,7 +35,6 @@ public:
void setQuantity(int quantity);
void setPrice(double price);
void setState(util::State status);
std::string serialize() const;
static InventoryItem* deserialize(const std::string&);
static std::string getHeaders();
SerializedInventoryItem serialize() const;
static InventoryItem* deserialize(const SerializedInventoryItem&);
};
@@ -57,18 +57,19 @@ Returns:
*/
void InventoryManagementService::sendLowStockAlerts()
{
auto& inventoryItems = m_dataStore.getInventoryItems();
if (inventoryItems.isEmpty())
DataStoreLockGuard lock(m_dataStore);
auto& trackedInventoryItemsMap = m_dataStore.getInventoryItems();
auto& trackedUserMap = m_dataStore.getUsers();
if (trackedInventoryItemsMap.isEmpty())
{
return;
}
int inventoryItemsSize = inventoryItems.getSize();
auto& usersMap = m_dataStore.getUsers();
int usersMapSize = usersMap.getSize();
int inventoryItemsSize = trackedInventoryItemsMap.getSize();
int usersMapSize = trackedUserMap.getSize();
util::Vector<User*> adminUsers;
for (int index = 0; index < usersMapSize; index++)
{
User* user = usersMap.getValueAt(index);
User* user = trackedUserMap.getValueAt(index).data;
if (user->getUserType() == util::UserType::ADMIN)
{
adminUsers.push_back(user);
@@ -81,7 +82,7 @@ void InventoryManagementService::sendLowStockAlerts()
}
for (int index = 0; index < inventoryItemsSize; index++)
{
InventoryItem* inventoryItem = inventoryItems.getValueAt(index);
InventoryItem* inventoryItem = trackedInventoryItemsMap.getValueAt(index).data;
if (inventoryItem && inventoryItem->getQuantity() < config::threshold::INVENTORY_LOW_STOCK_THRESHOLD)
{
sendLowStockAlertsToAdmins(*this, inventoryItem, adminUsers);
@@ -100,8 +101,11 @@ Return type: void
*/
void InventoryManagementService::addInventoryItem(const std::string& partName, int quantity, double price)
{
DataStoreLockGuard lock(m_dataStore);
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
InventoryItem* newItem = Factory::getObject<InventoryItem>(partName, quantity, price);
m_dataStore.getInventoryItems().insert(newItem->getId(), newItem);
trackedInventoryItemMap.insert(newItem->getId(), util::createNewRecord(newItem));
m_dataStore.saveInventoryItems();
}
/*
@@ -113,16 +117,22 @@ Return type: void
*/
void InventoryManagementService::addInventoryItemStock(const std::string& selectedItemId, int quantity)
{
int index = m_dataStore.getInventoryItems().find(selectedItemId);
if (index != -1)
DataStoreLockGuard lock(m_dataStore);
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
int index = trackedInventoryItemMap.find(selectedItemId);
if (index == -1)
{
InventoryItem* item = m_dataStore.getInventoryItems().getValueAt(index);
if (item != nullptr)
throw std::runtime_error("Inventory update failed: Item ID '" + selectedItemId + "' not found.");
}
InventoryItem* item = trackedInventoryItemMap.getValueAt(index).data;
if (item == nullptr)
{
throw std::runtime_error("Inventory update failed. Item does not exist.\n");
}
int totalQuantity = item->getQuantity() + quantity;
item->setQuantity(totalQuantity);
}
}
trackedInventoryItemMap.getValueAt(index).state = RecordState::MODIFIED;
m_dataStore.saveInventoryItems();
}
/*
@@ -133,7 +143,10 @@ Return type: util::Map<std::string, InventoryItem*>
*/
util::Map<std::string, InventoryItem*> InventoryManagementService::getInventoryItems()
{
return m_dataStore.getInventoryItems();
DataStoreLockGuard lock(m_dataStore);
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
auto inventoryMap = util::getObjects(trackedInventoryItemMap);
return inventoryMap;
}
/*
@@ -144,15 +157,21 @@ Return type: void
*/
void InventoryManagementService::removeInventoryItem(const std::string& inventoryItemID)
{
int index = m_dataStore.getInventoryItems().find(inventoryItemID);
if (index != -1)
DataStoreLockGuard lock(m_dataStore);
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
int index = trackedInventoryItemMap.find(inventoryItemID);
if (index == -1)
{
InventoryItem* item = m_dataStore.getInventoryItems().getValueAt(index);
if (item != nullptr)
throw std::runtime_error("Inventory removal failed: Item ID '" + inventoryItemID + "' not found.");
}
InventoryItem* item = trackedInventoryItemMap.getValueAt(index).data;
if (item == nullptr)
{
throw std::runtime_error("Inventory removal failed: Item ID does not exist.");
}
item->setState(util::State::INACTIVE);
}
}
trackedInventoryItemMap.getValueAt(index).state = RecordState::MODIFIED;
m_dataStore.saveInventoryItems();
}
/*
@@ -163,12 +182,19 @@ Return type: InventoryItem*
*/
InventoryItem* InventoryManagementService::getInventoryItem(const std::string& inventoryItemID)
{
int index = m_dataStore.getInventoryItems().find(inventoryItemID);
if (index != -1)
DataStoreLockGuard lock(m_dataStore);
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
int index = trackedInventoryItemMap.find(inventoryItemID);
if (index == -1)
{
return m_dataStore.getInventoryItems().getValueAt(index);
}
return nullptr;
}
InventoryItem* inventoryItem = trackedInventoryItemMap.getValueAt(index).data;
if (inventoryItem == nullptr)
{
throw std::runtime_error("Item ID does not exist.");
}
return inventoryItem;
}
/*