Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete Inventory Management Module Implementation #929

Merged
merged 1 commit into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
343 changes: 343 additions & 0 deletions backend/controllers/shop/sub-controllers/inventoryController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
const Inventory = require("../../../model/shop/sub-model/InventoryModel");
const Product = require("../../../model/shop/product");
const Supplier = require("../../../model/shop/sub-model/SuppierModel");
const mongoose = require("mongoose");


exports.createInventoryItem = async (req, res) => {
try {
const {
productId,
currentStockLevel,
reorderLevel,
warehouseLocation,
supplierId,
performedBy,
} = req.body;

// Ensure product exists
const product = await Product.findById(productId);
if (!product) {
return res.status(400).json({ message: "Product not found" });
}

// Optional: Check if supplier exists (if provided)
if (supplierId) {
const supplier = await Supplier.findById(supplierId);
if (!supplier) {
return res.status(400).json({ message: "Supplier not found" });
}
}

const newInventoryItem = new Inventory({
productId,
currentStockLevel,
reorderLevel,
warehouseLocation,
supplierId,
});

// Adding audit log for creation
newInventoryItem.auditLog.push({
action: "Inventory Item Created",
performedBy,
quantityChanged: currentStockLevel,
notes: `Initial stock for ${product.name}`,
});

await newInventoryItem.save();
res
.status(201)
.json({
message: "Inventory item created successfully",
newInventoryItem,
});
} catch (error) {
res.status(500).json({ message: "Error creating inventory item", error });
}
};

// Controller for updating stock level
exports.updateStockLevel = async (req, res) => {
try {
const { inventoryId, newStockLevel, performedBy, notes } = req.body;

if (newStockLevel < 0) {
return res
.status(400)
.json({ message: "Stock level cannot be negative" });
}

const inventory = await Inventory.findById(inventoryId);
if (!inventory) {
return res.status(404).json({ message: "Inventory item not found" });
}

const quantityChanged = newStockLevel - inventory.currentStockLevel;
inventory.currentStockLevel = newStockLevel;
inventory.auditLog.push({
action: "Stock Level Updated",
performedBy,
quantityChanged,
notes,
});

await inventory.save();
res
.status(200)
.json({ message: "Stock level updated successfully", inventory });
} catch (error) {
res.status(500).json({ message: "Error updating stock level", error });
}
};

// Controller to fetch inventory by product
exports.fetchInventoryByProduct = async (req, res) => {
try {
const { productId } = req.params;
const inventoryItem = await Inventory.findOne({ productId })
.populate("productId")
.populate("supplierId");

if (!inventoryItem) {
return res
.status(404)
.json({ message: "Inventory not found for this product" });
}

res.status(200).json({ inventoryItem });
} catch (error) {
res
.status(500)
.json({ message: "Error fetching inventory by product", error });
}
};

// Controller to fetch inventory by warehouse
exports.fetchInventoryByWarehouse = async (req, res) => {
try {
const { warehouseLocation } = req.params;
const inventoryItems = await Inventory.find({ warehouseLocation })
.populate("productId")
.populate("supplierId");

if (!inventoryItems || inventoryItems.length === 0) {
return res
.status(404)
.json({ message: "No inventory found for this warehouse" });
}

res.status(200).json({ inventoryItems });
} catch (error) {
res
.status(500)
.json({ message: "Error fetching inventory by warehouse", error });
}
};

// Controller to fetch all inventory
exports.fetchAllInventory = async (req, res) => {
try {
const inventoryItems = await Inventory.find()
.populate("productId")
.populate("supplierId");

if (!inventoryItems || inventoryItems.length === 0) {
return res.status(404).json({ message: "No inventory items found" });
}

res.status(200).json({ inventoryItems });
} catch (error) {
res.status(500).json({ message: "Error fetching all inventory", error });
}
};

// Controller to delete an inventory item
exports.deleteInventoryItem = async (req, res) => {
try {
const { inventoryId } = req.params;
const inventory = await Inventory.findById(inventoryId);

if (!inventory) {
return res.status(404).json({ message: "Inventory item not found" });
}

await inventory.remove();
res.status(200).json({ message: "Inventory item deleted successfully" });
} catch (error) {
res.status(500).json({ message: "Error deleting inventory item", error });
}
};

// Controller for bulk updating stock levels
exports.bulkUpdateStockLevels = async (req, res) => {
try {
const { updates, performedBy } = req.body;

const updatedItems = [];
for (const update of updates) {
const { inventoryId, newStockLevel, notes } = update;
const inventory = await Inventory.findById(inventoryId);

if (!inventory) {
return res
.status(404)
.json({ message: `Inventory item with ID ${inventoryId} not found` });
}

const quantityChanged = newStockLevel - inventory.currentStockLevel;
inventory.currentStockLevel = newStockLevel;
inventory.auditLog.push({
action: "Stock Level Updated (Bulk)",
performedBy,
quantityChanged,
notes,
});

await inventory.save();
updatedItems.push(inventory);
}

res
.status(200)
.json({
message: "Bulk stock levels updated successfully",
updatedItems,
});
} catch (error) {
res
.status(500)
.json({ message: "Error updating stock levels in bulk", error });
}
};

// Controller for bulk creation of inventory items
exports.bulkCreateInventoryItems = async (req, res) => {
try {
const { items, performedBy } = req.body;
const createdItems = [];

for (const item of items) {
const {
productId,
currentStockLevel,
reorderLevel,
warehouseLocation,
supplierId,
} = item;

// Ensure product exists
const product = await Product.findById(productId);
if (!product) {
return res
.status(400)
.json({ message: `Product with ID ${productId} not found` });
}

// Optional: Check if supplier exists (if provided)
if (supplierId) {
const supplier = await Supplier.findById(supplierId);
if (!supplier) {
return res
.status(400)
.json({ message: `Supplier with ID ${supplierId} not found` });
}
}

const newInventoryItem = new Inventory({
productId,
currentStockLevel,
reorderLevel,
warehouseLocation,
supplierId,
});

newInventoryItem.auditLog.push({
action: "Inventory Item Created (Bulk)",
performedBy,
quantityChanged: currentStockLevel,
notes: `Initial stock for ${product.name}`,
});

await newInventoryItem.save();
createdItems.push(newInventoryItem);
}

res
.status(201)
.json({
message: "Bulk inventory items created successfully",
createdItems,
});
} catch (error) {
res
.status(500)
.json({ message: "Error creating inventory items in bulk", error });
}
};

// Controller for logging actions in inventory audit trail
exports.logInventoryAction = async (req, res) => {
try {
const { inventoryId, action, performedBy, quantityChanged, notes } =
req.body;

const inventory = await Inventory.findById(inventoryId);
if (!inventory) {
return res.status(404).json({ message: "Inventory item not found" });
}

inventory.auditLog.push({
action,
performedBy,
quantityChanged,
notes,
});

await inventory.save();
res.status(200).json({ message: "Action logged successfully" });
} catch (error) {
res.status(500).json({ message: "Error logging action", error });
}
};

// Controller for handling expired inventory (e.g., perishable items)
exports.handleExpiredInventory = async (req, res) => {
try {
const { productId } = req.params;
const expiredInventory = await Inventory.find({
productId,
currentStockLevel: { $gt: 0 },
lastRestockedDate: { $lt: new Date() },
});

if (expiredInventory.length === 0) {
return res
.status(404)
.json({ message: "No expired inventory found for this product" });
}

for (const inventory of expiredInventory) {
inventory.currentStockLevel = 0; // Mark stock as zero
inventory.auditLog.push({
action: "Expired Inventory Removed",
performedBy: "System",
quantityChanged: -inventory.currentStockLevel,
notes: "Inventory marked as expired",
});

await inventory.save();
}

res
.status(200)
.json({
message: "Expired inventory handled successfully",
expiredInventory,
});
} catch (error) {
res
.status(500)
.json({ message: "Error handling expired inventory", error });
}
};
38 changes: 38 additions & 0 deletions backend/middleware/sub-ware/inventoryMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const Inventory = require("../../model/shop/sub-model/InventoryModel");
const Product = require("../../model/shop/product");

// Middleware to validate if product exists before creating inventory
exports.validateProductExistence = async (req, res, next) => {
const { productId } = req.body;

const product = await Product.findById(productId);
if (!product) {
return res.status(400).json({ message: "Product not found" });
}

next();
};

// Middleware to ensure valid stock level when updating
exports.validateStockLevel = (req, res, next) => {
const { newStockLevel } = req.body;

if (newStockLevel < 0) {
return res.status(400).json({ message: "Stock level cannot be negative" });
}

next();
};

// Middleware to ensure reorder level is logical (not negative)
exports.validateReorderLevel = (req, res, next) => {
const { reorderLevel } = req.body;

if (reorderLevel < 0) {
return res
.status(400)
.json({ message: "Reorder level cannot be negative" });
}

next();
};
Loading
Loading