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.
238 lines
6.8 KiB
238 lines
6.8 KiB
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
|
|
};
|
|
|