diff --git a/controllers/CompartimentController.js b/controllers/CompartimentController.js new file mode 100644 index 0000000..67d2613 --- /dev/null +++ b/controllers/CompartimentController.js @@ -0,0 +1,51 @@ +const { pool } = require('../config/databases'); + +exports.createCompartiment = async (req, res) => { + const {name, capacity, uniter, id_emplacement,} = req.body; + + if (!name) { + return res.status(400).json({ message: 'Name are required.' }); + } else if (!capacity) { + return res.status(400).json({ message: 'Capacity is required.' }); + } else if (!uniter) { + return res.status(400).json({ message: 'Uniter is required.' }); + } + + // verify if emplacement exists + try { + const [emplacement] = await pool.query('SELECT * FROM emplacements WHERE id = ?', [id_emplacement]); + + if (emplacement.length === 0) { + return res.status(404).json({ message: 'Emplacement not found.' }); + } + + // Insert compartiment + const [result] = await pool.query('INSERT INTO compartiments (name, capacity, uniter, id_emplacement) VALUES (?, ?, ?, ?)', [name, capacity, uniter, id_emplacement]); + + res.status(201).json({ + message: 'Compartiment created successfully', + compartiment: { + id: result.insertId, + name, + capacity, + uniter, + id_emplacement, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + } + }); + } catch (error) { + console.error(error); + return res.status(500).json({ message: 'Server error while creating compartiment.' }); + } +} + +exports.getCompartiments = async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM compartiments'); + res.json(rows); + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error while fetching compartiments.' }); + } +} \ No newline at end of file diff --git a/controllers/CustomersController.js b/controllers/CustomersController.js new file mode 100644 index 0000000..42c18d3 --- /dev/null +++ b/controllers/CustomersController.js @@ -0,0 +1,144 @@ +const { pool } = require('../config/databases'); +const { validationResult } = require('express-validator'); + +// Lister les clients avec recherche et stats +exports.index = async (req, res) => { + const query = req.query.query || ''; + try { + let [clients] = query + ? await pool.query( + `SELECT * FROM clients WHERE name LIKE ? OR email LIKE ?`, + [`%${query}%`, `%${query}%`] + ) + : await db.query(`SELECT * FROM clients`); + + const [totalClientsResult] = await pool.query(`SELECT COUNT(*) as total FROM clients`); + const [loyalResult] = await pool.query(`SELECT COUNT(*) as loyal FROM clients WHERE membership IN ('Or', 'Argent')`); + const [bronzeResult] = await pool.query(`SELECT COUNT(*) as bronze FROM clients WHERE membership = 'Bronze'`); + const [argentResult] = await pool.query(`SELECT COUNT(*) as argent FROM clients WHERE membership = 'Argent'`); + const [orResult] = await pool.query(`SELECT COUNT(*) as gold FROM clients WHERE membership = 'Or'`); + + const avgSatisfaction = '4.6/5'; // À calculer depuis table avis + const avgCart = '€45'; // À calculer depuis commandes + + const fidelityLevels = { + Bronze: { + count: bronzeResult[0].bronze, + benefits: ['5% de réduction', 'Offre anniversaire'] + }, + Argent: { + count: argentResult[0].argent, + benefits: ['10% de réduction', 'Réservation prioritaire', 'Menu dégustation offert'] + }, + Or: { + count: orResult[0].gold, + benefits: ['15% de réduction', 'Service VIP', 'Événements exclusifs', 'Livraison gratuite'] + } + }; + + const marketingCampaigns = [ + { + name: 'Newsletter hebdomadaire', + description: 'Envoyée tous les vendredis', + subscribers: 1247, + type: 'newsletter' + }, + { + name: 'Offre spéciale Saint-Valentin', + description: 'SMS ciblé', + target: 342, + type: 'sms' + } + ]; + + res.json({ + searchQuery: query, + clients, + totalClients: totalClientsResult[0].total, + loyalClients: loyalResult[0].loyal, + avgSatisfaction, + avgCart, + fidelityLevels, + marketingCampaigns + }); + } catch (error) { + res.status(500).json({ error: 'Erreur serveur', details: error.message }); + } +}; + +// Ajouter un nouveau client avec validation +exports.save = async (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) + return res.status(400).json({ errors: errors.array() }); + + const { name, email, phone, visits, spent, rating, preferences } = req.body; + + let membership = ''; + if (visits >= 20 && spent >= 1000000) membership = 'Or'; + else if (visits >= 10 && spent >= 500000) membership = 'Argent'; + else if (visits >= 10 && spent >= 300000) membership = 'Bronze'; + + try { + await pool.query( + `INSERT INTO clients (name, email, phone, visits, spent, rating, membership, preferences) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [ + name, + email, + phone || '', + visits, + spent, + rating, + membership, + JSON.stringify(preferences || []) + ] + ); + + res.status(201).json({ message: `Client "${name}" ajouté avec succès !` }); + } catch (err) { + res.status(500).json({ error: 'Erreur serveur', details: err.message }); + } +}; + +// Afficher les détails d'un client +exports.show = async (req, res) => { + const id = req.params.id; + try { + const [result] = await pool.query(`SELECT * FROM clients WHERE id = ?`, [id]); + + if (result.length === 0) + return res.status(404).json({ error: 'Client non trouvé' }); + + const client = result[0]; + + res.json({ + niveauAdhesion: client.membership, + evaluation: client.rating + }); + } catch (err) { + res.status(500).json({ error: 'Erreur serveur', details: err.message }); + } +}; + +// Éditer un client (renvoyer les infos + moyenne des avis) +exports.edit = async (req, res) => { + const clientId = req.params.id; + try { + const [result] = await pool.query(`SELECT * FROM clients WHERE id = ?`, [clientId]); + + if (result.length === 0) + return res.status(404).json({ error: 'Client non trouvé' }); + + const client = result[0]; + const [noteResult] = await pool.query(`SELECT AVG(note) as note FROM avis WHERE client_id = ?`, [clientId]); + const evaluation = noteResult[0].note || 0; + + res.json({ + client, + evaluation + }); + } catch (err) { + res.status(500).json({ error: 'Erreur serveur', details: err.message }); + } +}; \ No newline at end of file diff --git a/controllers/EmplacementController.js b/controllers/EmplacementController.js new file mode 100644 index 0000000..9cfe9b6 --- /dev/null +++ b/controllers/EmplacementController.js @@ -0,0 +1,82 @@ +const { pool } = require('../config/databases'); + +exports.createEmplacement = async (req, res) => { + const { name, type, temperature, capacity } = req.body; + + if (!name || !type) { + return res.status(400).json({ message: 'Name and type are required.' }); + } else if (!temperature) { + return res.status(400).json({ message: 'Temperature is required.' }); + } else if (!capacity) { + return res.status(400).json({ message: 'Capacity is required.' }); + } + + try { + const [result] = await pool.query('INSERT INTO emplacements (name, type, temperature, capacity) VALUES (?, ?, ?, ?)', [name, type, temperature, capacity]); + + res.status(201).json({ + message: 'Emplacement created successfully', + emplacement: { + id: result.insertId, + name, + type, + temperature, + capacity, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + } + }); + } catch (error) { + console.error(error); + return res.status(500).json({ message: 'Server error while creating emplacement.' }); + } +} + +exports.getEmplacements = async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM emplacements'); + res.json(rows); + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error while fetching emplacements.' }); + } +} + +exports.getEmplacementById = async (req, res) => { + const { id } = req.params; + + try { + const [rows] = await pool.query('SELECT * FROM emplacements WHERE id = ?', [id]); + + if (rows.length === 0) { + return res.status(404).json({ message: 'Emplacement not found.' }); + } + + res.json(rows[0]); + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error while fetching emplacement.' }); + } +} + +exports.updateEmplacement = async (req, res) => { + const { id } = req.params; + const { name, type, temperature, capacity } = req.body; + + if (!name || !type || !temperature || !capacity) { + return res.status(400).json({ message: 'All fields are required.' }); + } + + try { + const [result] = await pool.query('UPDATE emplacements SET name = ?, type = ?, temperature = ?, capacity = ? WHERE id = ?', [name, type, temperature, capacity, id]); + + if (result.affectedRows === 0) { + return res.status(404).json({ message: 'Emplacement not found.' }); + } + + res.json({ message: 'Emplacement updated successfully' }); + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error while updating emplacement.' }); + } +} \ No newline at end of file diff --git a/controllers/FinancesController.js b/controllers/FinancesController.js new file mode 100644 index 0000000..4c9f70d --- /dev/null +++ b/controllers/FinancesController.js @@ -0,0 +1,238 @@ +const { pool } = require('../config/databases'); + +// GET /finances +const getDashboard = async (req, res) => { + try { + const [payments] = await pool.query(` + SELECT id, description, payment_date AS date_transaction, 'Paiement' AS type_transaction, 'Recette' AS categorie, amount + FROM payments + ORDER BY payment_date DESC + LIMIT 10 + `); + + const [[{ totalRevenue }]] = await pool.query(`SELECT SUM(amount) AS totalRevenue FROM payments`); + + let expenses = [], totalExpenses = 0, depenses_change = 0; + + const [expensesTable] = await pool.query(`SHOW TABLES LIKE 'expenses'`); + if (expensesTable.length > 0) { + [expenses] = await db.query(` + SELECT id, description, expense_date AS date_transaction, 'Dépense' AS type_transaction, 'Dépense' AS categorie, amount + FROM expenses + ORDER BY expense_date DESC + LIMIT 10 + `); + + const [[{ total }]] = await pool.query(`SELECT SUM(amount) AS total FROM expenses`); + totalExpenses = total || 0; + + const previousMonthExpenses = 800000; + if (previousMonthExpenses > 0) { + depenses_change = ((totalExpenses - previousMonthExpenses) / previousMonthExpenses) * 100; + } + } + + const previousMonthRevenue = 2000000; + const chiffre_affaires_change = (previousMonthRevenue > 0) + ? ((totalRevenue - previousMonthRevenue) / previousMonthRevenue) * 100 + : 0; + + const benefice_net = totalRevenue - totalExpenses; + const benefice_net_marge = totalRevenue > 0 ? (benefice_net / totalRevenue) * 100 : 0; + const tresorerie = benefice_net; + + const dashboard_stats = { + chiffre_affaires: totalRevenue, + chiffre_affaires_change, + depenses: totalExpenses, + depenses_change, + benefices: benefice_net, + benefice_net, + benefice_net_marge, + tresorerie, + tresorerie_solde: tresorerie, + total_liabilities: 0 + }; + + const profitLoss = { + revenu_total: totalRevenue, + depense_total: totalExpenses, + benefice_net, + marge: benefice_net_marge + }; + + const balanceSheet = { + total_assets: 0, + total_liabilities: 0, + total_equity: 0, + net_worth: 0 + }; + + const transactions = [...payments, ...expenses].sort((a, b) => + new Date(b.date_transaction) - new Date(a.date_transaction) + ); + + const [[invoiceTable]] = await pool.query(`SHOW TABLES LIKE 'invoices'`); + let invoices = []; + if (invoiceTable) { + [invoices] = await pool.query(` + SELECT id, invoice_number, client_name, invoice_date, status, total_amount + FROM invoices + ORDER BY invoice_date DESC + LIMIT 10 + `); + } + + res.json({ + transactions, + dashboard_stats, + reportDate: new Date().toISOString().slice(0, 10), + profitLoss, + balanceSheet, + invoices + }); + } catch (err) { + console.error("Erreur dans getDashboard:", err.message); + res.status(500).json({ error: "Erreur serveur" }); + } +}; + +// POST /finances/invoices +const generateInvoice = async (req, res) => { + const { + invoice_number, + client_name, + client_email, + client_address, + invoice_date, + due_date, + total_amount, + status = 'Draft', + notes + } = req.body; + + try { + await pool.query(` + INSERT INTO invoices (invoice_number, client_name, client_email, client_address, invoice_date, due_date, total_amount, status, notes) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + `, [ + invoice_number.trim(), + client_name.trim(), + client_email?.trim() || null, + client_address.trim(), + invoice_date || new Date(), + due_date || null, + parseFloat(total_amount), + status.trim(), + notes?.trim() + ]); + + res.status(201).json({ message: 'Facture ajoutée avec succès.' }); + } catch (err) { + console.error("Erreur dans generateInvoice:", err.message); + res.status(500).json({ error: err.message }); + } +}; + +// POST /finances/payments +const processPayment = async (req, res) => { + const { invoice_id, payment_date, amount, payment_method, description } = req.body; + const conn = await pool.getConnection(); + + try { + await conn.beginTransaction(); + + await conn.query(` + INSERT INTO payments (invoice_id, payment_date, amount, payment_method, description) + VALUES (?, ?, ?, ?, ?) + `, [ + invoice_id, + payment_date || new Date(), + parseFloat(amount), + payment_method.trim(), + description.trim() + ]); + + const [[{ total_amount }]] = await conn.query(`SELECT total_amount FROM invoices WHERE id = ?`, [invoice_id]); + const [[{ total_paid }]] = await conn.query(`SELECT SUM(amount) AS total_paid FROM payments WHERE invoice_id = ?`, [invoice_id]); + + let status = 'Partially Paid'; + if (Math.abs(total_amount - total_paid) < 0.01) { + status = 'Paid'; + } + + await conn.query(`UPDATE invoices SET status = ? WHERE id = ?`, [status, invoice_id]); + + await conn.query(` + UPDATE balance_sheet_accounts + SET amount = amount + ? + WHERE name = 'Trésorerie' AND account_type = 'Asset' + `, [amount]); + + await conn.commit(); + res.status(200).json({ message: 'Paiement enregistré avec succès.' }); + + } catch (err) { + await conn.rollback(); + console.error("Erreur dans processPayment:", err.message); + res.status(500).json({ error: err.message }); + } finally { + conn.release(); + } +}; + +// GET + POST /finances/taxes +const getTaxes = async (req, res) => { + const [taxes] = await pool.query(`SELECT * FROM taxes ORDER BY name ASC`); + res.json(taxes); +}; + +const createOrUpdateTax = async (req, res) => { + const { id, name, rate, description, is_active } = req.body; + + if (!name || isNaN(rate)) { + return res.status(400).json({ error: 'Nom et taux valides requis.' }); + } + + try { + if (id) { + await pool.query(` + UPDATE taxes SET name = ?, rate = ?, description = ?, is_active = ? + WHERE id = ? + `, [name.trim(), rate, description?.trim(), is_active ? 1 : 0, id]); + } else { + await pool.query(` + INSERT INTO taxes (name, rate, description, is_active) + VALUES (?, ?, ?, ?) + `, [name.trim(), rate, description?.trim(), is_active ? 1 : 0]); + } + + res.json({ message: 'Taxe enregistrée.' }); + } catch (err) { + console.error("Erreur dans createOrUpdateTax:", err.message); + res.status(500).json({ error: err.message }); + } +}; + +// DELETE /finances/taxes/:id +const disableTax = async (req, res) => { + const id = parseInt(req.params.id); + if (!id) return res.status(400).json({ error: "ID invalide" }); + + try { + await pool.query(`UPDATE taxes SET is_active = 0 WHERE id = ?`, [id]); + res.json({ message: 'Taxe désactivée.' }); + } catch (err) { + console.error("Erreur dans disableTax:", err.message); + res.status(500).json({ error: err.message }); + } +}; + +module.exports = { + getDashboard, + generateInvoice, + processPayment, + getTaxes, + createOrUpdateTax, + disableTax +}; diff --git a/controllers/FournisseurController.js b/controllers/FournisseurController.js new file mode 100644 index 0000000..0afcffa --- /dev/null +++ b/controllers/FournisseurController.js @@ -0,0 +1,41 @@ +const { pool } = require('../config/databases'); + +exports.createFournisseur = async (req, res) => { + const { name, category, contact_person, phone, email, status } = req.body; + + if (!name || !category || !contact_person || !phone || !email || !status) { + return res.status(400).json({ message: 'All fields are required.' }); + } + + try { + const [result] = await pool.query('INSERT INTO fournisseurs (name, category, contact_person, phone, email, status) VALUES(?, ?, ?, ?, ?, ?)', [name, category, contact_person, phone, email, status]); + + res.status(201).json({ + message: 'Fournisseur created successfully', + fournisseur: { + id: result.insertId, + name, + category, + contact_person, + phone, + email, + status, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + } + }); + } catch (error) { + console.error(error); + return res.status(500).json({ message: 'Server error while creating ingredient.' }); + } +} + +exports.getFournisseurs = async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM fournisseurs'); + res.json(rows); + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error while fetching fournisseurs.' }); + } +} \ No newline at end of file diff --git a/controllers/ModulesController.js b/controllers/ModulesController.js new file mode 100644 index 0000000..b699f1e --- /dev/null +++ b/controllers/ModulesController.js @@ -0,0 +1,59 @@ +const { pool } = require('../config/databases'); + +// Afficher la liste des modules avec leur état pour un établissement donné +exports.index = async (req, res) => { + try { + const etablissementId = 1; // À remplacer par auth ou token utilisateur + + const [modules] = await pool.query(`SELECT * FROM modules`); + const [actifsRaw] = await pool.query(` + SELECT module_id, est_actif + FROM etablissement_modules + WHERE etablissement_id = ?`, + [etablissementId] + ); + + const actifs = {}; + actifsRaw.forEach(row => { + actifs[row.module_id] = !!row.est_actif; + }); + + res.render('modules/index', { modules, actifs }); + } catch (err) { + res.status(500).json({ error: 'Erreur serveur', details: err.message }); + } +}; + +// Activer/désactiver un module (AJAX) +exports.toggle = async (req, res) => { + const { id, actif } = req.body; + const moduleId = parseInt(id, 10); + const etablissementId = 1; // Authentification future ici + + try { + const [existing] = await pool.query( + `SELECT id FROM etablissement_modules + WHERE module_id = ? AND etablissement_id = ?`, + [moduleId, etablissementId] + ); + + if (existing.length > 0) { + await pool.query( + `UPDATE etablissement_modules + SET est_actif = ? + WHERE id = ?`, + [actif ? 1 : 0, existing[0].id] + ); + } else { + await pool.query( + `INSERT INTO etablissement_modules (module_id, etablissement_id, est_actif) + VALUES (?, ?, ?)`, + [moduleId, etablissementId, actif ? 1 : 0] + ); + } + + res.json({ success: true }); + } catch (err) { + res.status(500).json({ success: false, error: err.message }); + } +}; diff --git a/controllers/ReservtionController.js b/controllers/ReservtionController.js new file mode 100644 index 0000000..284f8fd --- /dev/null +++ b/controllers/ReservtionController.js @@ -0,0 +1,147 @@ +const reservationModel = require('../models/reservationModel'); +const tableModel = require('../models/tableModel'); +const invoiceModel = require('../models/invoiceModel'); + +// Helper pour filtrer selon la période +const filterByPeriod = (reservations, period) => { + const today = new Date(); + const todayStr = today.toISOString().slice(0, 10); + + return reservations.filter((res) => { + const resDate = new Date(res.date_reservation).toISOString().slice(0, 10); + + switch (period) { + case 'aujourdhui': + return resDate === todayStr; + case 'demain': + const tomorrow = new Date(today); + tomorrow.setDate(today.getDate() + 1); + return resDate === tomorrow.toISOString().slice(0, 10); + case 'cette-semaine': + const day = today.getDay(); + const diffToMonday = today.getDate() - day + (day === 0 ? -6 : 1); + const monday = new Date(today.setDate(diffToMonday)); + const sunday = new Date(monday); + sunday.setDate(monday.getDate() + 6); + + return new Date(res.date_reservation) >= monday && new Date(res.date_reservation) <= sunday; + case 'toutes': + default: + return true; + } + }); +}; + +// GET /reservations +const index = async (req, res) => { + const period = req.query.period || 'aujourdhui'; + const allReservations = await reservationModel.getAll(); + const tables = await tableModel.getAll(); + + const filteredReservations = filterByPeriod(allReservations, period); + + const confirmedReservations = filteredReservations.filter(r => r.statut_reservation === 'Confirmée').length; + const pendingReservations = filteredReservations.filter(r => r.statut_reservation === 'En attente').length; + const estimatedRevenue = filteredReservations.reduce((acc, r) => acc + (parseFloat(r.montant_estime_mga) || 0), 0); + + const reservedTables = [...new Set( + filteredReservations + .filter(r => r.type_reservation === 'table' && !['Annulée', 'Terminée'].includes(r.statut_reservation)) + .map(r => r.numero_table_chambre) + )].length; + + const occupancyRate = tables.length > 0 ? Math.round((reservedTables / tables.length) * 100) : 0; + + res.json({ + currentPeriod: period, + filteredReservations, + tables, + confirmedReservations, + pendingReservations, + estimatedRevenue, + occupancyRate + }); +}; + +// POST /reservations +const store = async (req, res) => { + try { + const data = req.body; + + const reservationId = await reservationModel.insert(data); + + if (reservationId) { + const lastInvoice = await invoiceModel.getLast(); + let newInvoiceNumber = 'F00001'; + + if (lastInvoice && lastInvoice.invoice_number.match(/F(\d+)/)) { + const num = parseInt(RegExp.$1); + newInvoiceNumber = 'F' + String(num + 1).padStart(5, '0'); + } + + await invoiceModel.insert({ + invoice_number: newInvoiceNumber, + client_name: data.client_nom, + client_email: data.email || null, + invoice_date: new Date(), + due_date: null, + total_amount: parseFloat(data.montant_estime_mga) || 0, + status: 'Draft', + reservation_id: reservationId, + notes: data.notes_speciales || null + }); + } + + res.status(201).json({ message: 'Réservation et facture ajoutées avec succès.' }); + } catch (err) { + console.error('Erreur dans store():', err.message); + res.status(500).json({ error: err.message }); + } +}; + +// DELETE /reservations/:id +const destroy = async (req, res) => { + const { id } = req.params; + await reservationModel.delete(id); + res.json({ message: 'Réservation supprimée.' }); +}; + +// GET /reservations/:id/edit +const edit = async (req, res) => { + const { id } = req.params; + const reservation = await reservationModel.findById(id); + if (!reservation) { + return res.status(404).json({ error: 'Réservation introuvable.' }); + } + res.json({ reservation }); +}; + +// PUT /reservations/:id +const update = async (req, res) => { + const { id } = req.params; + const data = req.body; + + if (!data.client_nom || data.client_nom.length < 3) { + return res.status(400).json({ error: 'Nom client invalide.' }); + } + + // Autres validations manuelles ici si besoin + + await reservationModel.update(id, data); + res.json({ message: 'Réservation mise à jour.' }); +}; + +// GET /reservations/create?table=3 +const create = (req, res) => { + const tableId = req.query.table; + res.json({ table: tableId }); +}; + +module.exports = { + index, + store, + destroy, + edit, + update, + create +}; diff --git a/controllers/SettingsController.js b/controllers/SettingsController.js new file mode 100644 index 0000000..e69de29 diff --git a/controllers/StockController.js b/controllers/StockController.js new file mode 100644 index 0000000..e08ddfc --- /dev/null +++ b/controllers/StockController.js @@ -0,0 +1,58 @@ +const { pool } = require('../config/databases'); + +exports.createIngredient = async (req, res) => { + const { articles, quantity, uniter, price_unit, id_emplacement, id_compartiment } = req.body; + + if (!articles || !quantity || !uniter || !price_unit) { + return res.status(400).json({ message: 'All fields are required.' }); + } + + // verify if compartiment exists + try { + const [compartiment] = await pool.query('SELECT * FROM compartiments WHERE id = ?', [id_compartiment]); + + if (compartiment.length === 0) { + return res.status(404).json({ message: 'Compartiment not found.' }); + } + + // Insert ingredient + const [result] = await pool.query('INSERT INTO stocks (articles, quantity, uniter, price_unit, id_emplacement, id_compartiment) VALUES (?, ?, ?, ?, ?, ?)', [articles, quantity, uniter, price_unit, id_emplacement, id_compartiment]); + + res.status(201).json({ + message: 'Ingredient created successfully', + ingredient: { + id: result.insertId, + articles, + quantity, + uniter, + price_unit, + id_compartiment, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + } + }); + } catch (error) { + console.error(error); + return res.status(500).json({ message: 'Server error while creating ingredient.' }); + } +} + +exports.getIngredientsInventaire = async (req, res) => { + try { + const [rows] = await pool.query('SELECT emplacements.name, stocks.id as stock_id, stocks.articles, stocks.uniter, stocks.quantity, stocks.id_emplacement, stocks.id_compartiment FROM emplacements JOIN stocks ON emplacements.id = stocks.id_emplacement'); + res.json(rows); + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error while fetching ingredients.' }); + } +} + +exports.getIngredientsEmplacements = async (req, res) => { + try { + const [rows] = await pool.query('SELECT emplacements.name AS emplacement_name, emplacements.id AS emplacement_id, emplacements.type AS emplacement_type, emplacements.temperature, emplacements.capacity AS emplacement_capacity, compartiments.id AS compartiment_id, compartiments.name AS compartiment_name, compartiments.capacity AS compartiment_capacity, compartiments.uniter AS compartiment_uniter, stocks.id AS stock_id, stocks.articles, stocks.quantity, stocks.uniter AS stock_uniter, stocks.price_unit FROM emplacements JOIN compartiments ON emplacements.id = compartiments.id_emplacement JOIN stocks ON compartiments.id = stocks.id_compartiment'); + res.json(rows); + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error while fetching ingredients.' }); + } +} \ No newline at end of file diff --git a/controllers/TablesController.js b/controllers/TablesController.js new file mode 100644 index 0000000..de973b0 --- /dev/null +++ b/controllers/TablesController.js @@ -0,0 +1,106 @@ +const { pool } = require('../config/databases'); + +exports.index = async (req, res) => { + try { + const [tables] = await pool.query('SELECT * FROM tables'); + res.render('tables/index', { tables }); + } catch (err) { + res.status(500).send('Erreur serveur : ' + err.message); + } +}; + +// Affiche formulaire création (rendu simple, adapter selon moteur de vue) +exports.create = (req, res) => { + res.render('tables/create'); +}; + +// Enregistre nouvelle table +exports.store = async (req, res) => { + const { numero_table, capacite, statut_actuel } = req.body; + + try { + await pool.query( + `INSERT INTO tables (numero_table, capacite, statut_actuel, id_reservation_actuelle) + VALUES (?, ?, ?, NULL)`, + [numero_table, capacite, statut_actuel] + ); + res.redirect('/tables?success=Table ajoutée avec succès.'); + } catch (err) { + // En cas d'erreur, tu peux passer l'erreur en query string ou gérer autrement + res.redirect('/tables/create?error=' + encodeURIComponent(err.message)); + } +}; + +// Affiche formulaire édition +exports.edit = async (req, res) => { + const id = req.params.id; + try { + const [rows] = await pool.query('SELECT * FROM tables WHERE id = ?', [id]); + if (rows.length === 0) { + return res.status(404).send('Table non trouvée'); + } + res.render('tables/edit', { table: rows[0] }); + } catch (err) { + res.status(500).send('Erreur serveur : ' + err.message); + } +}; + +// Met à jour une table +exports.update = async (req, res) => { + const id = req.params.id; + const data = req.body; + + try { + // Mettre à jour la table + const [result] = await pool.query( + `UPDATE tables SET numero_table = ?, capacite = ?, statut_actuel = ? WHERE id = ?`, + [data.numero_table, data.capacite, data.statut_actuel, id] + ); + res.redirect('/tables?success=Table modifiée avec succès.'); + } catch (err) { + res.redirect(`/tables/${id}/edit?error=` + encodeURIComponent(err.message)); + } +}; + +// Supprimer une table +exports.delete = async (req, res) => { + const id = req.params.id; + try { + await pool.query('DELETE FROM tables WHERE id = ?', [id]); + res.redirect('/tables?success=Table supprimée.'); + } catch (err) { + res.status(500).send('Erreur serveur : ' + err.message); + } +}; + +// Afficher détails JSON d'une table + client réservation en cours +exports.show = async (req, res) => { + const id = req.params.id; + try { + const [tables] = await pool.query('SELECT * FROM tables WHERE id = ?', [id]); + if (tables.length === 0) { + return res.status(404).json({ error: 'Table non trouvée' }); + } + const table = tables[0]; + + + const [reservations] = await pool.query(` + SELECT client_nom FROM reservations + WHERE type_reservation = 'table' + AND numero_table_chambre = ? + AND statut_reservation IN ('Confirmée', 'En attente') + LIMIT 1 + `, [table.numero_table]); + + const reservation = reservations.length > 0 ? reservations[0] : {}; + + res.json({ + capacite: table.capacite, + details: table.details || 'N/A', + statut_actuel: table.statut_actuel, + client_nom: reservation.client_nom || null, + }); + } catch (err) { + res.status(500).json({ error: 'Erreur serveur', details: err.message }); + } +}; diff --git a/controllers/TransactionsController.js b/controllers/TransactionsController.js new file mode 100644 index 0000000..e266c0f --- /dev/null +++ b/controllers/TransactionsController.js @@ -0,0 +1,12 @@ +const { pool } = require('../config/databases'); + +exports.index = async (req, res) => { + try { + const [transactions] = await pool.query( + 'SELECT * FROM transactions ORDER BY date_transaction DESC' + ); + res.render('transactions/transactions_content', { transactions }); + } catch (err) { + res.status(500).send('Erreur serveur : ' + err.message); + } +}; diff --git a/routes/protectedRoute.js b/routes/protectedRoute.js index 45cd543..07d56e9 100644 --- a/routes/protectedRoute.js +++ b/routes/protectedRoute.js @@ -2,11 +2,32 @@ const express = require('express'); const authMiddleware = require('../middleware/authMiddleware'); const userController = require('../controllers/UserController'); const staffController = require('../controllers/staffsController'); +const emplacementController = require('../controllers/emplacementController'); +const compartimentController = require('../controllers/compartimentController'); +const stockController = require('../controllers/stockController'); +const fournisseurController = require('../controllers/FournisseurController'); +const reservationController = require('../controllers/ReservationController'); +const financesController = require('../controllers/FinancesController'); +const CustomersController = require('../controllers/CustomersController'); +const SettingsController = require('../controllers/SettingsController'); +const ModulesController = require('../controllers/ModulesController'); +const TablesController = require('../controllers/TablesController'); +const TransactionsController = require('../controllers/TransactionsController'); const router = express.Router(); router.get('/profile', authMiddleware(), userController.getProfile); router.get('/admin', authMiddleware('admin'), userController.getAdminPage); router.post('/create', authMiddleware('admin'), userController.createUser); +router.post('/create/emplacement', authMiddleware(), emplacementController.createEmplacement); +router.get('/emplacements', authMiddleware(), emplacementController.getEmplacements); +router.get('/emplacement/:id', authMiddleware(), emplacementController.getEmplacementById); +router.get('/compartiments', authMiddleware(), compartimentController.getCompartiments); +router.post('/create/compartiment', authMiddleware(), compartimentController.createCompartiment); +router.post('/create/ingredient', authMiddleware(), stockController.createIngredient); +router.get('/ingredients/inventaire', authMiddleware(), stockController.getIngredientsInventaire); +router.get('/ingredients/emplacement', authMiddleware(), stockController.getIngredientsEmplacements); +router.post('/create/fournisseur', authMiddleware(), fournisseurController.createFournisseur); +router.get('/fournisseurs', authMiddleware(), fournisseurController.getFournisseurs); router.get('/staffs', authMiddleware(), staffController.index); router.get('/staffs/create', authMiddleware('admin'), staffController.create); @@ -15,4 +36,59 @@ router.get('/staffs/:id/edit', authMiddleware(), staffController.edit); router.post('/staffs/:id/update', authMiddleware('admin'), staffController.update); router.get('/staffs/:id/contact', authMiddleware(), staffController.contact); router.get('/staffs/:employeId/statut/:date', authMiddleware(), staffController.calculerStatutEmploye); + +router.get('/reservations', authMiddleware(), reservationController.index); // équivalent de $routes->get('/reservations', ...) +router.get('/reservations/ajax', authMiddleware(), reservationController.getReservationsAjax); // si cette méthode existe +router.get('/reservations/create', authMiddleware(), reservationController.create); +router.post('/reservations/store', authMiddleware(), reservationController.store); +router.get('/reservations/:id/edit', authMiddleware(), reservationController.edit); +router.put('/reservations/:id', authMiddleware(), reservationController.update); +router.delete('/reservations/:id', authMiddleware(), reservationController.destroy); + +router.get('/finances', authMiddleware(), financesController.getDashboard); +router.post('/finances/invoices', authMiddleware(), financesController.generateInvoice); +router.post('/finances/payments', authMiddleware(), financesController.processPayment); +router.get('/finances/taxes', authMiddleware(), financesController.getTaxes); +router.post('/finances/taxes', authMiddleware(), financesController.createOrUpdateTax); +router.delete('/finances/taxes/:id', authMiddleware(), financesController.disableTax); + +router.get('/customers',authMiddleware(), CustomersController.index); // Liste des clients +router.get('/customers/new', (req, res) => res.send('Formulaire nouveau client')); // Optionnel +router.post('/customers/save', authMiddleware(), CustomersController.save); // Enregistrer un client +router.get('/customers/:id',authMiddleware(), CustomersController.show); +router.get('customers/:id/edit', authMiddleware(), CustomersController.edit); +router.put('customers/:id/uptade',authMiddleware(), CustomersController.update); +router.delete('customers/:id/delete',authMiddleware(), CustomersController.delete); + + +router.get('/settings', authMiddleware(), SettingsController.getSettingsPage); +router.post('/settings/restaurant', authMiddleware(), SettingsController.updateRestaurantInfo); +router.post('/settings/opening-hours', authMiddleware(), SettingsController.updateOpeningHours); +router.post('/settings/notifications', authMiddleware(), SettingsController.updateNotifications); +router.get('/settings/users/new', authMiddleware(), SettingsController.addUser); +router.post('/settings/users', authMiddleware(), SettingsController.saveUser); +router.get('/settings/users/:id/edit', authMiddleware(), SettingsController.editUser); +router.put('/settings/users/:id', authMiddleware(), SettingsController.updateUser); +router.delete('/settings/users/:id', authMiddleware(), SettingsController.deleteUser); +router.post('/settings/change-password', authMiddleware(), SettingsController.changePassword); +router.post('/settings/two-factor', authMiddleware(), SettingsController.toggleTwoFactorAuth); +router.get('/settings/integrations/booking', authMiddleware(), SettingsController.connectBooking); +router.get('/settings/integrations/quickbooks', authMiddleware(), SettingsController.connectQuickbooks); +router.get('/settings/integrations/stripe', authMiddleware(), SettingsController.connectStripe); +router.get('/settings/modules', authMiddleware(), SettingsController.getModules); + +router.get('/modules', authMiddleware(), ModulesController.index); +router.post('/modules/toggle', authMiddleware(), ModulesController.toggle); + +router.get('/tables', authMiddleware(), TablesController.index); +router.get('/tables/create', authMiddleware(), TablesController.create); +router.post('/tables', authMiddleware(), TablesController.store); +router.get('/tables/:id/edit', authMiddleware(), TablesController.edit); +router.post('/tables/:id/update', authMiddleware(), TablesController.update); +router.post('/tables/:id/delete', authMiddleware(), TablesController.delete); +router.get('/tables/:id', authMiddleware(), TablesController.show); + + +router.get('/transactions', authMiddleware(), TransactionsController.index); + module.exports = router;