Preview: inventoryController.js
Size: 12.15 KB
/home/byroehnu/easepay.easetack.com/controllers/inventoryController.js
const { prisma } = require('../config/prisma');
const inventoryController = {
// Get all inventory items
getAllItems: async (req, res) => {
try {
const userId = req.user.id;
const businessId = req.user.businessId;
const { page = 1, limit = 20, category, search, includeInactive = false } = req.query;
const whereClause = {
userId,
...(businessId && { businessId }),
...(category && { category }),
...(search && {
OR: [
{ name: { contains: search, mode: 'insensitive' } },
{ description: { contains: search, mode: 'insensitive' } }
]
}),
...(includeInactive === 'false' && { isActive: true })
};
const items = await prisma.inventoryItem.findMany({
where: whereClause,
orderBy: { name: 'asc' },
skip: (parseInt(page) - 1) * parseInt(limit),
take: parseInt(limit)
});
const totalItems = await prisma.inventoryItem.count({ where: whereClause });
res.json({
success: true,
data: {
items,
pagination: {
currentPage: parseInt(page),
totalPages: Math.ceil(totalItems / parseInt(limit)),
totalItems,
limit: parseInt(limit)
}
}
});
} catch (error) {
console.error('Error getting inventory items:', error);
res.status(500).json({
success: false,
message: 'Error getting inventory items',
error: error.message
});
}
},
// Get single inventory item
getItem: async (req, res) => {
try {
const { id } = req.params;
const userId = req.user.id;
const item = await prisma.inventoryItem.findFirst({
where: { id, userId },
include: {
invoiceItems: {
include: {
invoice: {
select: {
id: true,
invoiceNumber: true,
customerName: true,
invoiceDate: true,
total: true
}
}
},
orderBy: { createdAt: 'desc' },
take: 10
}
}
});
if (!item) {
return res.status(404).json({
success: false,
message: 'Item not found'
});
}
res.json({
success: true,
data: item
});
} catch (error) {
console.error('Error getting inventory item:', error);
res.status(500).json({
success: false,
message: 'Error getting inventory item',
error: error.message
});
}
},
// Create new inventory item
createItem: async (req, res) => {
try {
const userId = req.user.id;
const businessId = req.user.businessId;
const {
name,
description,
category,
unitPrice,
costPrice,
stock = 0,
unit,
minStock
} = req.body;
// Validate required fields
if (!name || !unitPrice) {
return res.status(400).json({
success: false,
message: 'Name and unit price are required'
});
}
const item = await prisma.inventoryItem.create({
data: {
name,
description,
category,
unitPrice: parseFloat(unitPrice),
costPrice: costPrice ? parseFloat(costPrice) : null,
stock: parseFloat(stock),
unit,
minStock: minStock ? parseFloat(minStock) : null,
userId,
businessId
}
});
res.status(201).json({
success: true,
message: 'Item created successfully',
data: item
});
} catch (error) {
console.error('Error creating inventory item:', error);
res.status(500).json({
success: false,
message: 'Error creating inventory item',
error: error.message
});
}
},
// Update inventory item
updateItem: async (req, res) => {
try {
const { id } = req.params;
const userId = req.user.id;
const updateData = req.body;
// Check if item exists and belongs to user
const existingItem = await prisma.inventoryItem.findFirst({
where: { id, userId }
});
if (!existingItem) {
return res.status(404).json({
success: false,
message: 'Item not found'
});
}
// Convert numeric fields
if (updateData.unitPrice) updateData.unitPrice = parseFloat(updateData.unitPrice);
if (updateData.costPrice) updateData.costPrice = parseFloat(updateData.costPrice);
if (updateData.stock !== undefined) updateData.stock = parseFloat(updateData.stock);
if (updateData.minStock) updateData.minStock = parseFloat(updateData.minStock);
const updatedItem = await prisma.inventoryItem.update({
where: { id },
data: updateData
});
res.json({
success: true,
message: 'Item updated successfully',
data: updatedItem
});
} catch (error) {
console.error('Error updating inventory item:', error);
res.status(500).json({
success: false,
message: 'Error updating inventory item',
error: error.message
});
}
},
// Delete inventory item
deleteItem: async (req, res) => {
try {
const { id } = req.params;
const userId = req.user.id;
const item = await prisma.inventoryItem.findFirst({
where: { id, userId }
});
if (!item) {
return res.status(404).json({
success: false,
message: 'Item not found'
});
}
await prisma.inventoryItem.delete({
where: { id }
});
res.json({
success: true,
message: 'Item deleted successfully'
});
} catch (error) {
console.error('Error deleting inventory item:', error);
res.status(500).json({
success: false,
message: 'Error deleting inventory item',
error: error.message
});
}
},
// Update stock for multiple items
updateStock: async (req, res) => {
try {
const userId = req.user.id;
const { updates } = req.body; // Array of { id, quantity, operation: 'add' | 'subtract' | 'set' }
if (!Array.isArray(updates) || updates.length === 0) {
return res.status(400).json({
success: false,
message: 'Updates array is required'
});
}
const results = [];
for (const update of updates) {
const { id, quantity, operation = 'set' } = update;
const item = await prisma.inventoryItem.findFirst({
where: { id, userId }
});
if (!item) {
results.push({ id, success: false, message: 'Item not found' });
continue;
}
let newStock;
switch (operation) {
case 'add':
newStock = parseFloat(item.stock) + parseFloat(quantity);
break;
case 'subtract':
newStock = parseFloat(item.stock) - parseFloat(quantity);
break;
case 'set':
default:
newStock = parseFloat(quantity);
break;
}
if (newStock < 0) {
results.push({ id, success: false, message: 'Stock cannot be negative' });
continue;
}
const updatedItem = await prisma.inventoryItem.update({
where: { id },
data: { stock: newStock }
});
results.push({ id, success: true, newStock: updatedItem.stock });
}
res.json({
success: true,
message: 'Stock update completed',
data: results
});
} catch (error) {
console.error('Error updating stock:', error);
res.status(500).json({
success: false,
message: 'Error updating stock',
error: error.message
});
}
},
// Get low stock items
getLowStockItems: async (req, res) => {
try {
const userId = req.user.id;
const businessId = req.user.businessId;
const lowStockItems = await prisma.inventoryItem.findMany({
where: {
userId,
...(businessId && { businessId }),
isActive: true,
minStock: { not: null },
OR: [
{
stock: { lte: { minStock: true } }
}
]
},
orderBy: { stock: 'asc' }
});
res.json({
success: true,
data: {
lowStockItems,
count: lowStockItems.length
}
});
} catch (error) {
console.error('Error getting low stock items:', error);
res.status(500).json({
success: false,
message: 'Error getting low stock items',
error: error.message
});
}
},
// Get inventory categories
getCategories: async (req, res) => {
try {
const userId = req.user.id;
const businessId = req.user.businessId;
const categories = await prisma.inventoryItem.findMany({
where: {
userId,
...(businessId && { businessId }),
category: { not: null },
isActive: true
},
select: {
category: true
},
distinct: ['category']
});
const categoryList = categories
.map(item => item.category)
.filter(category => category && category.trim() !== '')
.sort();
res.json({
success: true,
data: {
categories: categoryList
}
});
} catch (error) {
console.error('Error getting categories:', error);
res.status(500).json({
success: false,
message: 'Error getting categories',
error: error.message
});
}
},
// Get inventory analytics
getInventoryAnalytics: async (req, res) => {
try {
const userId = req.user.id;
const businessId = req.user.businessId;
const whereClause = {
userId,
...(businessId && { businessId }),
isActive: true
};
const analytics = await prisma.inventoryItem.aggregate({
where: whereClause,
_count: { id: true },
_sum: {
stock: true,
unitPrice: true,
costPrice: true
},
_avg: {
unitPrice: true,
stock: true
}
});
const categoryBreakdown = await prisma.inventoryItem.groupBy({
by: ['category'],
where: whereClause,
_count: { category: true },
_sum: { stock: true, unitPrice: true }
});
// Calculate total inventory value
const inventoryValue = await prisma.inventoryItem.findMany({
where: whereClause,
select: {
stock: true,
unitPrice: true,
costPrice: true
}
});
const totalValue = inventoryValue.reduce((sum, item) => {
return sum + (parseFloat(item.stock) * parseFloat(item.unitPrice));
}, 0);
const totalCost = inventoryValue.reduce((sum, item) => {
return sum + (parseFloat(item.stock) * parseFloat(item.costPrice || 0));
}, 0);
res.json({
success: true,
data: {
totalItems: analytics._count.id,
totalStock: analytics._sum.stock || 0,
averagePrice: analytics._avg.unitPrice || 0,
averageStock: analytics._avg.stock || 0,
totalInventoryValue: totalValue,
totalInventoryCost: totalCost,
potentialProfit: totalValue - totalCost,
categoryBreakdown
}
});
} catch (error) {
console.error('Error getting inventory analytics:', error);
res.status(500).json({
success: false,
message: 'Error getting inventory analytics',
error: error.message
});
}
}
};
module.exports = inventoryController;
Directory Contents
Dirs: 1 × Files: 17