controller_bill_controller.js
/**
* @fileoverview Contrôleur gérant les opérations CRUD sur les factures
* @module controllers/bill
*/
const Bill = require('../models/bill_model')
const User = require('../models/user_model')
const { uploadToS3, deleteFromS3 } = require('../utils/s3')
/**
* Récupère toutes les factures
* @function getBills
* @async
* @param {Object} req - L'objet requête Express
* @param {Object} req.user - L'utilisateur authentifié
* @param {string} req.user.id - L'ID de l'utilisateur
* @param {string} req.user.role - Le rôle de l'utilisateur
* @param {Object} res - L'objet réponse Express
* @returns {Promise<Object[]>} Liste des factures
* @throws {Error} Erreur lors de la récupération des factures
*/
const getBills = async(req,res) => {
try {
const {id, role} = req.user
let bills
if(role === 'admin'){
bills = await Bill.find({})
const billsWithEmail = await Promise.all(bills.map(async (bill) => {
const user = await User.findOne({_id: bill.user}, {_id: 0, email: 1, name: 1})
return {
...bill.toObject(),
email: user ? user.email : null,
name: user ? user.name : null
}
}))
return res.json(billsWithEmail)
} else {
bills = await Bill.find({user: id})
return res.json(bills)
}
}
catch (error) {
res.status(500).json({message: error.message})
}
}
/**
* Récupère une facture par son ID
* @function getBillsById
* @async
* @param {Object} req - L'objet requête Express
* @param {Object} req.params - Les paramètres de la requête
* @param {string} req.params._id - L'ID de la facture
* @param {Object} res - L'objet réponse Express
* @returns {Promise<Object>} La facture trouvée
* @throws {Error} Erreur si la facture n'est pas trouvée
*/
const getBillsById = async(req,res) => {
try{
const bill = await Bill.findOne({_id : req.params._id})
if(!bill){
res.status(404).json({message: 'Bill not found'})
}else{
res.json(bill)}}
catch (error) {
res.status(500).json({message: error.message})
}
}
/**
* Crée une nouvelle facture
* @function createBill
* @async
* @param {Object} req - L'objet requête Express
* @param {Object} req.body - Le corps de la requête
* @param {Object} req.body.metadata - Les métadonnées de la facture
* @param {string} req.body.metadata.date - La date de la facture
* @param {number} req.body.metadata.amount - Le montant de la facture
* @param {string} req.body.metadata.description - La description de la facture
* @param {string} req.body.metadata.status - Le statut de la facture
* @param {string} req.body.metadata.type - Le type de la facture
* @param {Object} req.file - Le fichier justificatif
* @param {Object} req.user - L'utilisateur authentifié
* @param {string} req.user.id - L'ID de l'utilisateur
* @param {Object} res - L'objet réponse Express
* @returns {Promise<Object>} La facture créée
* @throws {Error} Erreur lors de la création de la facture
*/
const createBill = async(req, res) => {
try {
const {date, amount, description, status, type} = JSON.parse(req.body.metadata)
const {id} = req.user
let proofUrl = null
if (req.file)
{
proofUrl = await uploadToS3(req.file)
} else {
throw new Error('Proof file is required', {cause: 400})
}
const bill = new Bill({
date,
amount,
description,
proof : proofUrl,
status,
type,
user: id
})
await bill.save()
res.status(201).json(bill)
} catch (error) {
res.status(500).json({message: error.message})
}
}
/**
* Supprime une facture
* @function deleteBill
* @async
* @param {Object} req - L'objet requête Express
* @param {Object} req.params - Les paramètres de la requête
* @param {string} req.params._id - L'ID de la facture à supprimer
* @param {Object} res - L'objet réponse Express
* @returns {Promise<Object>} Message de confirmation
* @throws {Error} Erreur lors de la suppression
*/
const deleteBill = async(req, res) => {
try {
if (!req.params._bill || !req.params._bill.match(/^[0-9a-fA-F]{24}$/)) {
return res.status(400).json({message: 'ID de facture invalide'});
}
const bill = await Bill.findByIdAndDelete(req.params._bill); // On utilise _bill
if(!bill) {
return res.status(404).json({message: 'Bill not found'});
}
// On supprime le fichier de preuve de S3
if (bill.proof) {
try {
await deleteFromS3(bill.proof);
} catch (error) {
console.error(`Erreur lors de la suppression du fichier S3 pour la facture ${bill._id}:`, error);
}
}
res.status(200).json({
message: 'Bill deleted',
deletedBill: bill._id
});
}
catch (error) {
res.status(500).json({message: error.message});
}
}
/**
* Met à jour une facture
* @function updateBill
* @async
* @param {Object} req - L'objet requête Express
* @param {Object} req.params - Les paramètres de la requête
* @param {string} req.params._id - L'ID de la facture à mettre à jour
* @param {Object} req.body - Le corps de la requête
* @param {Object} req.body.metadata - Les métadonnées de la facture
* @param {Object} req.file - Le nouveau fichier justificatif (optionnel)
* @param {Object} res - L'objet réponse Express
* @returns {Promise<Object>} La facture mise à jour
* @throws {Error} Erreur lors de la mise à jour
*/
const updateBill = async(req, res) => {
try {
const existingBill = await Bill.findById(req.params._id);
if (!existingBill) {
return res.status(404).json({message: 'Facture non trouvée'});
}
// On parse les metadata comme dans createBill
const {date, amount, description, status, type} = JSON.parse(req.body.metadata);
// On prépare l'objet de mise à jour
const updateFields = {
date,
amount,
description,
status,
type
};
// Si on a un nouveau fichier, on l'upload sur S3
if (req.file) {
const proofUrl = await uploadToS3(req.file);
updateFields.proof = proofUrl;
}
const updatedBill = await Bill.findByIdAndUpdate(
req.params._id,
{ $set: updateFields },
{ new: true }
);
res.status(200).json(updatedBill);
}
catch (error) {
console.error('Erreur lors de la mise à jour:', error);
res.status(500).json({message: error.message});
}
}
/**
* Supprime plusieurs factures
* @function deleteManyBills
* @async
* @param {Object} req - L'objet requête Express
* @param {Object} req.body - Le corps de la requête
* @param {string[]} req.body.ids - Liste des IDs des factures à supprimer
* @param {Object} res - L'objet réponse Express
* @returns {Promise<Object>} Résultat de la suppression
* @throws {Error} Erreur lors de la suppression multiple
*/
const deleteManyBills = async(req, res) => {
try {
const { ids } = req.body;
if (!Array.isArray(ids)) {
return res.status(400).json({ message: 'Les IDs doivent être fournis dans un tableau' });
}
// Récupérer d'abord toutes les factures pour avoir les URLs des preuves
const bills = await Bill.find({ _id: { $in: ids } });
// Supprimer les fichiers dans S3
for (const bill of bills) {
if (bill.proof) {
try {
await deleteFromS3(bill.proof);
} catch (error) {
console.error(`Erreur lors de la suppression du fichier S3 pour la facture ${bill._id}:`, error);
}
}
}
// Supprimer les factures de la base de données
const result = await Bill.deleteMany({ _id: { $in: ids } });
if (result.deletedCount === 0) {
return res.status(404).json({ message: 'Aucune facture trouvée' });
}
res.status(200).json({
message: `${result.deletedCount} factures ont été supprimées avec succès`,
deletedCount: result.deletedCount
});
}
catch (error) {
console.error('Erreur lors de la suppression multiple:', error);
res.status(500).json({ message: error.message });
}
}
module.exports = {getBills, createBill, deleteBill, updateBill, getBillsById, deleteManyBills}