You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

635 lines
20 KiB

const Table = require('../models/Table');
const { Op } = require('sequelize');
class TableController {
// Get all tables with optional filtering
static async getAllTables(req, res) {
try {
console.log('🔍 TableController.getAllTables - Début');
const {
status,
location,
capacity_min,
capacity_max,
page = 1,
limit = 10,
sort = 'id',
order = 'ASC'
} = req.query;
const offset = (page - 1) * limit;
const whereClause = {};
// Apply filters
if (status) {
whereClause.status = status;
}
if (location) {
whereClause.location = {
[Op.like]: `%${location}%`
};
}
if (capacity_min || capacity_max) {
whereClause.capacity = {};
if (capacity_min) whereClause.capacity[Op.gte] = parseInt(capacity_min);
if (capacity_max) whereClause.capacity[Op.lte] = parseInt(capacity_max);
}
console.log('Where conditions:', whereClause);
const { count, rows } = await Table.findAndCountAll({
where: whereClause,
limit: parseInt(limit),
offset: parseInt(offset),
order: [[sort, order.toUpperCase()]]
});
console.log(`✅ Found ${count} tables, returning ${rows.length} tables`);
res.json({
success: true,
data: rows,
pagination: {
total: count,
page: parseInt(page),
limit: parseInt(limit),
totalPages: Math.ceil(count / limit)
}
});
} catch (error) {
console.error('❌ Error in getAllTables:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch tables',
message: error.message
});
}
}
// Get table by ID
static async getTableById(req, res) {
try {
const { id } = req.params;
if (!id || isNaN(id)) {
return res.status(400).json({
success: false,
error: 'Valid table ID is required'
});
}
const table = await Table.findByPk(id);
if (!table) {
return res.status(404).json({
success: false,
error: 'Table not found'
});
}
res.json({
success: true,
data: table
});
} catch (error) {
console.error('❌ Error in getTableById:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch table',
message: error.message
});
}
}
// Create new table
static async createTable(req, res) {
try {
const { nom, capacity, status, location } = req.body;
// Validation
if (!nom) {
return res.status(400).json({
success: false,
error: 'Table name is required'
});
}
if (nom.length > 100) {
return res.status(400).json({
success: false,
error: 'Table name must be less than 100 characters'
});
}
if (capacity && (capacity < 1 || capacity > 20)) {
return res.status(400).json({
success: false,
error: 'Capacity must be between 1 and 20'
});
}
if (status && !['available', 'occupied', 'reserved', 'maintenance'].includes(status)) {
return res.status(400).json({
success: false,
error: 'Status must be: available, occupied, reserved, or maintenance'
});
}
if (location && location.length > 50) {
return res.status(400).json({
success: false,
error: 'Location must be less than 50 characters'
});
}
// Check if table name already exists
const existingTable = await Table.findOne({
where: { nom }
});
if (existingTable) {
return res.status(409).json({
success: false,
error: 'Table name already exists'
});
}
const table = await Table.create({
nom,
capacity: capacity || 4,
status: status || 'available',
location: location || null
});
res.status(201).json({
success: true,
data: table,
message: 'Table created successfully'
});
} catch (error) {
console.error('❌ Error in createTable:', error);
if (error.name === 'SequelizeValidationError') {
return res.status(400).json({
success: false,
error: 'Validation error',
details: error.errors.map(err => ({
field: err.path,
message: err.message
}))
});
}
if (error.name === 'SequelizeUniqueConstraintError') {
return res.status(409).json({
success: false,
error: 'Table name already exists'
});
}
res.status(500).json({
success: false,
error: 'Failed to create table',
message: error.message
});
}
}
// Update table
static async updateTable(req, res) {
try {
const { id } = req.params;
const { nom, capacity, status, location } = req.body;
if (!id || isNaN(id)) {
return res.status(400).json({
success: false,
error: 'Valid table ID is required'
});
}
const table = await Table.findByPk(id);
if (!table) {
return res.status(404).json({
success: false,
error: 'Table not found'
});
}
// Validation
if (nom !== undefined) {
if (!nom || nom.length === 0) {
return res.status(400).json({
success: false,
error: 'Table name cannot be empty'
});
}
if (nom.length > 100) {
return res.status(400).json({
success: false,
error: 'Table name must be less than 100 characters'
});
}
// Check if new name already exists (excluding current table)
const existingTable = await Table.findOne({
where: {
nom,
id: { [Op.ne]: id }
}
});
if (existingTable) {
return res.status(409).json({
success: false,
error: 'Table name already exists'
});
}
}
if (capacity !== undefined && (capacity < 1 || capacity > 20)) {
return res.status(400).json({
success: false,
error: 'Capacity must be between 1 and 20'
});
}
if (status && !['available', 'occupied', 'reserved', 'maintenance'].includes(status)) {
return res.status(400).json({
success: false,
error: 'Status must be: available, occupied, reserved, or maintenance'
});
}
if (location !== undefined && location && location.length > 50) {
return res.status(400).json({
success: false,
error: 'Location must be less than 50 characters'
});
}
// Update fields
const updateData = {};
if (nom !== undefined) updateData.nom = nom;
if (capacity !== undefined) updateData.capacity = capacity;
if (status !== undefined) updateData.status = status;
if (location !== undefined) updateData.location = location;
await table.update(updateData);
res.json({
success: true,
data: table,
message: 'Table updated successfully'
});
} catch (error) {
console.error('❌ Error in updateTable:', error);
if (error.name === 'SequelizeValidationError') {
return res.status(400).json({
success: false,
error: 'Validation error',
details: error.errors.map(err => ({
field: err.path,
message: err.message
}))
});
}
if (error.name === 'SequelizeUniqueConstraintError') {
return res.status(409).json({
success: false,
error: 'Table name already exists'
});
}
res.status(500).json({
success: false,
error: 'Failed to update table',
message: error.message
});
}
}
// Delete table
static async deleteTable(req, res) {
try {
const { id } = req.params;
if (!id || isNaN(id)) {
return res.status(400).json({
success: false,
error: 'Valid table ID is required'
});
}
const table = await Table.findByPk(id);
if (!table) {
return res.status(404).json({
success: false,
error: 'Table not found'
});
}
// Check if table is currently occupied or reserved
if (table.status === 'occupied' || table.status === 'reserved') {
return res.status(400).json({
success: false,
error: 'Cannot delete table that is currently occupied or reserved'
});
}
await table.destroy();
res.json({
success: true,
message: 'Table deleted successfully'
});
} catch (error) {
console.error('❌ Error in deleteTable:', error);
res.status(500).json({
success: false,
error: 'Failed to delete table',
message: error.message
});
}
}
// Get tables by status
static async getTablesByStatus(req, res) {
try {
const { status } = req.params;
const validStatuses = ['available', 'occupied', 'reserved', 'maintenance'];
if (!validStatuses.includes(status)) {
return res.status(400).json({
success: false,
error: `Invalid status. Must be: ${validStatuses.join(', ')}`
});
}
const tables = await Table.findAll({
where: { status },
order: [['nom', 'ASC']]
});
res.json({
success: true,
data: tables,
count: tables.length
});
} catch (error) {
console.error('❌ Error in getTablesByStatus:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch tables by status',
message: error.message
});
}
}
// Update table status
static async updateTableStatus(req, res) {
try {
const { id } = req.params;
const { status } = req.body;
if (!id || isNaN(id)) {
return res.status(400).json({
success: false,
error: 'Valid table ID is required'
});
}
const validStatuses = ['available', 'occupied', 'reserved', 'maintenance'];
if (!status || !validStatuses.includes(status)) {
return res.status(400).json({
success: false,
error: `Valid status is required (${validStatuses.join(', ')})`
});
}
const table = await Table.findByPk(id);
if (!table) {
return res.status(404).json({
success: false,
error: 'Table not found'
});
}
const oldStatus = table.status;
await table.update({ status });
res.json({
success: true,
data: table,
message: `Table status updated from ${oldStatus} to ${status}`
});
} catch (error) {
console.error('❌ Error in updateTableStatus:', error);
res.status(500).json({
success: false,
error: 'Failed to update table status',
message: error.message
});
}
}
// Get table statistics
static async getTableStats(req, res) {
try {
const totalTables = await Table.count();
const statusStats = await Table.findAll({
attributes: [
'status',
[Table.sequelize.fn('COUNT', Table.sequelize.col('status')), 'count']
],
group: ['status'],
raw: true
});
const capacityStats = await Table.findOne({
attributes: [
[Table.sequelize.fn('SUM', Table.sequelize.col('capacity')), 'total_capacity'],
[Table.sequelize.fn('AVG', Table.sequelize.col('capacity')), 'avg_capacity'],
[Table.sequelize.fn('MIN', Table.sequelize.col('capacity')), 'min_capacity'],
[Table.sequelize.fn('MAX', Table.sequelize.col('capacity')), 'max_capacity']
],
raw: true
});
const locationStats = await Table.findAll({
where: {
location: { [Op.ne]: null }
},
attributes: [
'location',
[Table.sequelize.fn('COUNT', Table.sequelize.col('location')), 'count']
],
group: ['location'],
raw: true
});
res.json({
success: true,
data: {
total: totalTables,
statusBreakdown: statusStats.reduce((acc, stat) => {
acc[stat.status] = parseInt(stat.count);
return acc;
}, {}),
locationBreakdown: locationStats.reduce((acc, stat) => {
acc[stat.location] = parseInt(stat.count);
return acc;
}, {}),
capacityStats: {
total: parseInt(capacityStats?.total_capacity) || 0,
average: parseFloat(capacityStats?.avg_capacity) || 0,
min: parseInt(capacityStats?.min_capacity) || 0,
max: parseInt(capacityStats?.max_capacity) || 0
}
}
});
} catch (error) {
console.error('❌ Error in getTableStats:', error);
res.status(500).json({
success: false,
error: 'Failed to get table statistics',
message: error.message
});
}
}
// Search tables
static async searchTables(req, res) {
try {
const { q, status, location, capacity_min, limit = 10 } = req.query;
if (!q || q.length < 2) {
return res.status(400).json({
success: false,
error: 'Search term must be at least 2 characters long'
});
}
const whereClause = {
[Op.or]: [
{ nom: { [Op.like]: `%${q}%` } },
{ location: { [Op.like]: `%${q}%` } }
]
};
if (status) {
whereClause.status = status;
}
if (location) {
whereClause.location = { [Op.like]: `%${location}%` };
}
if (capacity_min) {
whereClause.capacity = { [Op.gte]: parseInt(capacity_min) };
}
const tables = await Table.findAll({
where: whereClause,
limit: parseInt(limit),
order: [['nom', 'ASC']]
});
res.json({
success: true,
data: tables,
count: tables.length
});
} catch (error) {
console.error('❌ Error in searchTables:', error);
res.status(500).json({
success: false,
error: 'Failed to search tables',
message: error.message
});
}
}
// Get tables by location
static async getTablesByLocation(req, res) {
try {
const { location } = req.params;
if (!location) {
return res.status(400).json({
success: false,
error: 'Location parameter is required'
});
}
const tables = await Table.findAll({
where: {
location: { [Op.like]: `%${location}%` }
},
order: [['nom', 'ASC']]
});
res.json({
success: true,
data: tables,
count: tables.length
});
} catch (error) {
console.error('❌ Error in getTablesByLocation:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch tables by location',
message: error.message
});
}
}
// Get available tables with optional capacity filter
static async getAvailableTables(req, res) {
try {
const { capacity_min, location } = req.query;
const whereClause = {
status: 'available'
};
if (capacity_min) {
whereClause.capacity = { [Op.gte]: parseInt(capacity_min) };
}
if (location) {
whereClause.location = { [Op.like]: `%${location}%` };
}
const tables = await Table.findAll({
where: whereClause,
order: [['capacity', 'ASC'], ['nom', 'ASC']]
});
res.json({
success: true,
data: tables,
count: tables.length
});
} catch (error) {
console.error('❌ Error in getAvailableTables:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch available tables',
message: error.message
});
}
}
}
module.exports = TableController;