const API_BASE_URL_FACTURES = "[Link]
1:8000/api";
// CONFIGURATION RESPONSIVE
const RESPONSIVE_CONFIG = {
large: { // 17+ pouces
itemsPerPage: 15,
tableFontSize: '0.9rem',
showExport: true,
columns: ['numero', 'date', 'client', 'statut', 'montant', 'actions']
},
medium: { // 13-16 pouces
itemsPerPage: 12,
tableFontSize: '0.8rem',
showExport: true,
columns: ['numero', 'date', 'client', 'statut', 'montant', 'actions']
},
small: { // Tablettes
itemsPerPage: 10,
tableFontSize: '0.75rem',
showExport: false,
columns: ['numero', 'date', 'statut', 'montant', 'actions']
},
mobile: { // Mobiles
itemsPerPage: 8,
tableFontSize: '0.7rem',
showExport: false,
columns: ['numero', 'statut', 'montant', 'actions']
};
let currentConfig = RESPONSIVE_CONFIG.medium;
let ITEMS_PER_PAGE = [Link];
let currentPage = 1;
let allFactures = [];
let facturesFiltrees = [];
[Link](' [Link] chargé - Démarrage IMMÉDIAT');
// FONCTION POUR GÉNÉRER LE NUMÉRO DE FACTURE (RESET ANNUELLE)
function genererNumeroFacture() {
const maintenant = new Date();
const anneeActuelle = [Link]();
const mois = ([Link]() + 1).toString().padStart(2, '0');
const jour = [Link]().toString().padStart(2, '0');
// Format: FA_YYYY/MM/DD
const prefixe = `FA_${anneeActuelle}/${mois}/${jour}`;
// FILTRER LES FACTURES DE L'ANNÉE EN COURS SEULEMENT
const facturesAnneeCourante = [Link](facture => {
try {
const dateFacture = new Date([Link]);
return [Link]() === anneeActuelle;
} catch {
return false;
});
// Trouver le dernier numéro pour cette date dans l'année courante
const facturesDuJour = [Link](f =>
[Link](prefixe)
);
// Déterminer le prochain numéro séquentiel (RESET chaque année)
const prochainNumero = [Link] + 1;
return `${prefixe}_${[Link]().padStart(3, '0')}`;
// DÉTECTION AUTOMATIQUE DE LA TAILLE D'ÉCRAN
function getScreenSize() {
const width = [Link];
if (width >= 1600) return 'large';
if (width >= 1366) return 'medium';
if (width >= 768) return 'small';
return 'mobile';
// INITIALISATION RESPONSIVE
function initialiserResponsive() {
mettreAJourConfiguration();
[Link]('resize', function() {
clearTimeout([Link]);
[Link] = setTimeout(mettreAJourConfiguration, 250);
});
function mettreAJourConfiguration() {
const screenSize = getScreenSize();
currentConfig = RESPONSIVE_CONFIG[screenSize];
ITEMS_PER_PAGE = [Link];
appliquerStylesResponsives();
afficherFactures();
mettreAJourStatistiques();
mettreAJourPagination();
function appliquerStylesResponsives() {
const table = [Link]('facturesTable');
if (table) {
[Link] = [Link];
const exportBtn = [Link]('.export-btn');
if (exportBtn) {
[Link] = [Link] ? 'flex' : 'none';
// DÉMARRAGE IMMÉDIAT
function demarrerApplication() {
[Link](' Démarrage IMMÉDIAT de l\'application...');
const tbody = [Link]('facturesTableBody');
const stats = [Link]('statsSection');
const pagination = [Link]('paginationSection');
if (!tbody || !stats || !pagination) {
[Link](' Éléments manquants!');
setTimeout(demarrerApplication, 200);
return;
}
[Link](' Éléments trouvés!');
initialiserApplication();
function initialiserApplication() {
[Link](' Initialisation en cours...');
// Initialiser le responsive
initialiserResponsive();
// Configurer les événements
const statutFilter = [Link]('statutFilter');
const searchInput = [Link]('searchInput');
if (statutFilter) {
[Link]('change', filtrerFactures);
if (searchInput) {
[Link]('input', function() {
setTimeout(filtrerFactures, 300);
});
// Exposer les fonctions globales
[Link] = filtrerFactures;
[Link] = changerPage;
[Link] = fermerModal;
[Link] = afficherDetailsFacture;
[Link] = exporterFactures;
[Link] = payerFacture;
[Link] = confirmerPaiement;
[Link] = annulerPaiement;
[Link] = selectionnerModePaiement;
[Link] = modifierFacture;
[Link] = supprimerFacture;
[Link] = creerNouvelleFacture;
[Link] = confirmerAnnulation;
[Link](' Début du chargement des données...');
chargerToutesLesFactures();
// CHARGEMENT DES DONNÉES
async function chargerToutesLesFactures() {
[Link](' Chargement depuis API...');
try {
const response = await fetch(`${API_BASE_URL_FACTURES}/factures?statut=tous&limit=200`);
if ([Link]) {
const text = await [Link]();
const cleanedText = cleanApiResponse(text);
const result = [Link](cleanedText);
[Link](' Réponse API:', result);
if ([Link] && [Link]) {
allFactures = transformApiData([Link]);
[Link](` ${[Link]} factures chargées depuis API`);
afficherNotification(` ${[Link]} factures chargées`, 'success');
} else {
throw new Error('Données API invalides');
} else {
throw new Error('API non disponible');
} catch (error) {
[Link](' Erreur API:', error);
chargerDonneesTest();
return;
afficherFactures();
mettreAJourStatistiques();
mettreAJourPagination();
function chargerDonneesTest() {
[Link](' Chargement des données de test...');
allFactures = [
id: 1,
numeroFacture: "FA_202312_0001",
dateCreation: "2023-12-15 10:30:00",
societe: "SARL Madagascar Import",
numTicket: "TKT001",
statut: "payee",
montantTotal: 150000.0,
datePaiement: "2023-12-15 11:00:00",
motifAnnulation: null,
commentaire: null,
ventes: [
id: 1,
produit: { id: 1, nom: "Produit A" },
quantite: 2,
prix_unitaire: 50000,
prix_total: 100000,
num_ticket: "TKT001"
},
id: 2,
produit: { id: 2, nom: "Produit B" },
quantite: 1,
prix_unitaire: 50000,
prix_total: 50000,
num_ticket: "TKT001"
},
id: 2,
numeroFacture: "FA_202312_0002",
dateCreation: "2023-12-16 14:20:00",
societe: "EPIC Tana Distribution",
numTicket: "TKT002",
statut: "en_attente",
montantTotal: 75000.0,
datePaiement: null,
motifAnnulation: null,
commentaire: "Facture en attente de paiement",
ventes: [
id: 3,
produit: { id: 3, nom: "Produit C" },
quantite: 3,
prix_unitaire: 25000,
prix_total: 75000,
num_ticket: "TKT002"
},
id: 3,
numeroFacture: "FA_202312_0003",
dateCreation: "2023-12-17 09:15:00",
societe: "GIE Antsirabe Commerce",
numTicket: "TKT003",
statut: "annulee",
montantTotal: 120000.0,
datePaiement: null,
motifAnnulation: "Commande annulée par le client",
commentaire: "Facture annulée - Motif: Commande annulée par le client",
ventes: [
id: 4,
produit: { id: 4, nom: "Produit D" },
quantite: 4,
prix_unitaire: 30000,
prix_total: 120000,
num_ticket: "TKT003"
}
]
];
[Link](` ${[Link]} factures de test chargées`);
afficherNotification(' Données de test affichées', 'warning');
// TRANSFORMATION DES DONNÉES API
function transformApiData(apiData) {
[Link](' Transformation des données API...');
return [Link](facture => {
// Formater le numéro de facture selon les nouvelles règles
let numeroFactureFormate = facture.numero_facture || 'N/A';
// Remplacer les tirets par des underscores et supprimer l'heure si présent
if (numeroFactureFormate !== 'N/A') {
// Remplacer tous les tirets par des underscores
numeroFactureFormate = [Link](/-/g, '_');
// Supprimer l'heure si présente (format: YYYY_MM_DD_HH_MM_SS)
numeroFactureFormate =
[Link](/(\d{4}_\d{2}_\d{2})_\d{2}_\d{2}_\d{2}/, '$1');
// S'assurer que le numéro commence par FA_
if () {
numeroFactureFormate = 'FA_' + numeroFactureFormate;
}
return {
id: [Link] || 0,
numeroFacture: numeroFactureFormate,
dateCreation: facture.date_creation || new Date().toISOString(),
societe: [Link]?.nom || 'Client inconnu',
numTicket: facture.num_ticket || `TKT${[Link]}`,
statut: [Link] || 'en_attente',
montantTotal: facture.montant_total || 0,
datePaiement: facture.date_paiement || null,
motifAnnulation: facture.motif_annulation || null,
commentaire: [Link] || null, // INTÉGRATION DE LA COLONNE
COMMENTAIRE
ventes: [Link] ? [Link](vente => ({
id: [Link],
produit: [Link] ? {
id: [Link],
nom: [Link]
} : null,
quantite: [Link] || 1,
prix_unitaire: vente.prix_unitaire || 0,
prix_total: vente.prix_total || (vente.prix_unitaire * [Link]) || 0,
num_ticket: vente.num_ticket || ''
})) : []
};
});
// FONCTION POUR CRÉER UNE NOUVELLE FACTURE
async function creerNouvelleFacture() {
[Link](' Création nouvelle facture...');
try {
const nouveauNumero = genererNumeroFacture();
[Link](` Numéro généré: ${nouveauNumero}`);
// Appel API pour créer la facture
const response = await fetch(`${API_BASE_URL_FACTURES}/factures`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: [Link]({
numero_facture: nouveauNumero,
date_creation: new Date().toISOString(),
statut: 'en_attente',
montant_total: 0,
commentaire: 'Nouvelle facture créée' // COMMENTAIRE PAR DÉFAUT
})
});
if ([Link]) {
const text = await [Link]();
const result = [Link](cleanApiResponse(text));
if ([Link]) {
// Recharger les factures
await chargerToutesLesFactures();
afficherNotification(` Nouvelle facture ${nouveauNumero} créée`, 'success');
} else {
throw new Error([Link] || 'Erreur création facture');
}
} else {
// Simulation si l'API n'est pas disponible
const nouvelleFacture = {
id: [Link](...[Link](f => [Link])) + 1,
numeroFacture: nouveauNumero,
dateCreation: new Date().toISOString(),
societe: 'Nouveau client',
numTicket: `TKT${[Link](...[Link](f => parseInt([Link]('TKT', '')) ||
0)) + 1}`,
statut: 'en_attente',
montantTotal: 0,
datePaiement: null,
motifAnnulation: null,
commentaire: 'Nouvelle facture créée',
ventes: []
};
[Link](nouvelleFacture);
afficherFactures();
mettreAJourStatistiques();
mettreAJourPagination();
afficherNotification(` Nouvelle facture ${nouveauNumero} créée (simulation)`, 'success');
} catch (error) {
[Link](' Erreur création facture:', error);
afficherNotification(' Erreur lors de la création de la facture', 'error');
// AFFICHAGE PRINCIPAL RESPONSIVE
function afficherFactures() {
[Link](' Affichage du tableau responsive...');
const tbody = [Link]('facturesTableBody');
if (!tbody) return;
facturesFiltrees = [Link](facture => {
const statut = [Link]('statutFilter')?.value || 'tous';
const search = [Link]('searchInput')?.[Link]() || '';
if (statut !== 'tous' && [Link] !== statut) return false;
if (search && ![Link]().includes(search) &&
![Link]().includes(search)) return false;
return true;
});
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
const facturesPage = [Link](startIndex, startIndex + ITEMS_PER_PAGE);
[Link](` ${[Link]} factures, ${[Link]} sur la page`);
if ([Link] === 0) {
[Link] = `
<tr>
<td colspan="${[Link]}" style="padding: 30px; text-align: center;
color: #666;">
<i class="fas fa-file-invoice" style="font-size: 1.5rem; margin-bottom: 8px; display:
block;"></i>
<div style="font-size: 0.8rem;">Aucune facture trouvée</div>
</td>
</tr>
`;
return;
// CONSTRUCTION RESPONSIVE DES LIGNES - AVEC STYLES INLINE POUR LES BOUTONS
[Link] = [Link](facture => {
let rowHTML = '<tr style="border-bottom: 1px solid #2d2d2d;">';
if ([Link]('numero')) {
rowHTML += `
<td style="padding: 8px 10px; color: #e74c3c; font-weight: bold; font-family: monospace;">
${[Link]}
</td>
`;
if ([Link]('date')) {
rowHTML += `
<td style="padding: 8px 10px; color: #ccc;">
${formatDateTableau([Link])}
</td>
`;
if ([Link]('client')) {
rowHTML += `
<td style="padding: 8px 10px; color: #ccc; overflow: hidden; text-overflow: ellipsis; white-
space: nowrap;" title="${[Link]}">
${[Link]}
</td>
`;
}
if ([Link]('statut')) {
rowHTML += `
<td style="padding: 8px 10px; text-align: center;">
<span style="padding: 4px 8px; border-radius: 12px; font-size: 0.7rem; font-weight: bold;
text-transform: uppercase;
${[Link] === 'payee' ? 'background: #27ae60; color: white;' :
[Link] === 'en_attente' ? 'background: #f39c12; color: white;' :
'background: #e74c3c; color: white;'}">
${getStatutText([Link])}
</span>
${[Link] ? `
<br><small style="font-size: 0.6rem; color: #888; cursor: help;"
title="${[Link]}">
<i class="fas fa-info-circle"></i> motif
</small>
` : ''}
</td>
`;
if ([Link]('montant')) {
rowHTML += `
<td style="padding: 8px 10px; text-align: right; font-weight: bold; color: #fff;">
${formatMontantComplet([Link])}
</td>
`;
if ([Link]('actions')) {
rowHTML += `
<td style="padding: 8px 10px; text-align: center;">
<div style="display: flex; gap: 5px; justify-content: center; align-items: center;">
${[Link] === 'en_attente' ? `
<button onclick="payerFacture(${[Link]})"
style="width: 28px; height: 28px; border: none; border-radius: 4px; cursor:
pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; background:
#27ae60; color: white;"
onmouseover="[Link]='#219653';
[Link]='scale(1.1)'"
onmouseout="[Link]='#27ae60'; [Link]='scale(1)'"
title="Payer la facture">
<i class="fas fa-credit-card" style="font-size: 0.8rem;"></i>
</button>
` : ''}
<button onclick="afficherDetailsFacture(${[Link]})"
style="width: 28px; height: 28px; border: none; border-radius: 4px; cursor: pointer;
display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; background:
#3498db; color: white;"
onmouseover="[Link]='#2980b9'; [Link]='scale(1.1)'"
onmouseout="[Link]='#3498db'; [Link]='scale(1)'"
title="Voir les détails">
<i class="fas fa-eye" style="font-size: 0.8rem;"></i>
</button>
${[Link] !== 'annulee' ? `
<button onclick="modifierFacture(${[Link]})"
style="width: 28px; height: 28px; border: none; border-radius: 4px; cursor:
pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; background:
#f39c12; color: white;"
onmouseover="[Link]='#e67e22';
[Link]='scale(1.1)'"
onmouseout="[Link]='#f39c12'; [Link]='scale(1)'"
title="Modifier la facture">
<i class="fas fa-edit" style="font-size: 0.8rem;"></i>
</button>
<button onclick="supprimerFacture(${[Link]})"
style="width: 28px; height: 28px; border: none; border-radius: 4px; cursor:
pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; background:
#e74c3c; color: white;"
onmouseover="[Link]='#c0392b';
[Link]='scale(1.1)'"
onmouseout="[Link]='#e74c3c'; [Link]='scale(1)'"
title="Annuler la facture">
<i class="fas fa-ban" style="font-size: 0.8rem;"></i>
</button>
`:`
<button style="width: 28px; height: 28px; border: none; border-radius: 4px;
background: #666; color: #999; cursor: not-allowed;"
title="Facture annulée - non modifiable">
<i class="fas fa-lock" style="font-size: 0.8rem;"></i>
</button>
`}
</div>
</td>
`;
rowHTML += '</tr>';
return rowHTML;
}).join('');
[Link](' Tableau responsive mis à jour!');
}
// FONCTIONS POUR LES ACTIONS
function modifierFacture(factureId) {
[Link]('✏️ Modification facture:', factureId);
afficherNotification('Fonction de modification à implémenter', 'info');
// FONCTION DE SUPPRESSION/ANNULATION AMÉLIORÉE
async function supprimerFacture(factureId) {
[Link](' Annulation facture:', factureId);
const facture = [Link](f => [Link] === factureId);
if (!facture) {
afficherNotification('Facture non trouvée', 'error');
return;
if ([Link] === 'annulee') {
afficherNotification('Cette facture est déjà annulée', 'warning');
return;
// Afficher le modal de confirmation avec champ motif
[Link]('factureDetails').innerHTML = `
<div style="max-width: 500px;">
<h3 style="color: #e74c3c; margin-bottom: 15px; border-bottom: 2px solid #e74c3c; padding-
bottom: 8px; font-size: 1.1rem;">
<i class="fas fa-ban"></i> Annuler la Facture
</h3>
<div style="background: #2d2d2d; padding: 12px; border-radius: 4px; margin-bottom: 15px;">
<h4 style="color: #fff; margin-bottom: 10px; font-size: 0.9rem;">Détails de la facture</h4>
<div style="display: grid; gap: 6px; font-size: 0.8rem;">
<div><strong>N° Facture:</strong> ${[Link]}</div>
<div><strong>Client:</strong> ${[Link]}</div>
<div><strong>Date:</strong> ${formatDateTableau([Link])}</div>
<div><strong>Montant:</strong> ${formatMontantComplet([Link])}</div>
<div><strong>Statut actuel:</strong> <span style="color: ${[Link] === 'payee' ?
'#27ae60' : '#f39c12'}">${getStatutText([Link])}</span></div>
</div>
</div>
<div style="background: #2d2d2d; padding: 12px; border-radius: 4px; margin-bottom: 15px;">
<h4 style="color: #fff; margin-bottom: 10px; font-size: 0.9rem;">Motif de l'annulation</h4>
<textarea id="motifAnnulation"
placeholder="Veuillez saisir le motif de l'annulation de cette facture..."
style="width: 100%; height: 80px; background: #1a1a1a; border: 1px solid #444;
border-radius: 4px; color: #fff; padding: 8px; font-size: 0.8rem; resize: vertical;"
required></textarea>
<small style="color: #888; font-size: 0.7rem;">
<i class="fas fa-info-circle"></i> Ce motif sera enregistré dans la colonne COMMENTAIRE
de la base de données
</small>
</div>
<div style="display: flex; gap: 8px; justify-content: flex-end;">
<button onclick="fermerModal()"
style="background: #666; color: white; border: none; padding: 8px 16px; border-radius:
4px; cursor: pointer; font-size: 0.8rem;">
Retour
</button>
<button onclick="confirmerAnnulation(${factureId})"
style="background: #e74c3c; color: white; border: none; padding: 8px 16px; border-
radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.8rem;"
id="btnConfirmerAnnulation">
<i class="fas fa-ban"></i> Confirmer l'Annulation
</button>
</div>
</div>
`;
[Link]('factureModal').[Link] = 'block';
// FONCTION DE MODIFICATION AVEC CHOIX DE MODE DE PAIEMENT
async function modifierFacture(factureId) {
[Link]('✏️ Modification facture:', factureId);
const facture = [Link](f => [Link] === factureId);
if (!facture) {
afficherNotification('Facture non trouvée', 'error');
return;
// Vérifier si la facture peut être modifiée
if ([Link] === 'annulee') {
afficherNotification('Une facture annulée ne peut pas être modifiée', 'warning');
return;
// Options de mode de paiement avec icônes
const modesPaiement = [
{ value: null, label: 'Non spécifié', icon: 'fa-question-circle', color: '#888' },
{ value: 'especes', label: 'Espèces', icon: 'fa-money-bill-wave', color: '#27ae60' },
{ value: 'carte', label: 'Carte bancaire', icon: 'fa-credit-card', color: '#3498db' },
{ value: 'mobile_money', label: 'Mobile Money', icon: 'fa-mobile-alt', color: '#9b59b6' },
{ value: 'virement', label: 'Virement bancaire', icon: 'fa-university', color: '#f39c12' },
{ value: 'cheque', label: 'Chèque', icon: 'fa-file-invoice', color: '#1abc9c' },
{ value: 'autres', label: 'Autres', icon: 'fa-ellipsis-h', color: '#95a5a6' }
];
// HTML DU MODAL DE MODIFICATION AVEC CHOIX DE MODE DE PAIEMENT
[Link]('factureDetails').innerHTML = `
<div style="max-width: 500px;">
<h3 style="color: #3498db; margin-bottom: 15px; border-bottom: 2px solid #3498db; padding-
bottom: 8px; font-size: 1.1rem;">
<i class="fas fa-edit"></i> Modifier la Facture
</h3>
<div style="background: #2d2d2d; padding: 12px; border-radius: 4px; margin-bottom: 15px;">
<h4 style="color: #fff; margin-bottom: 10px; font-size: 0.9rem;">Informations de la
facture</h4>
<div style="display: grid; gap: 6px; font-size: 0.8rem;">
<div><strong>N° Facture:</strong> ${[Link]}</div>
<div><strong>Client:</strong> ${[Link]}</div>
<div><strong>Date création:</strong> ${formatDateTableau([Link])}</div>
<div><strong>Montant total:</strong>
${formatMontantComplet([Link])}</div>
<div><strong>Statut:</strong>
<span style="color: ${[Link] === 'payee' ? '#27ae60' : [Link] ===
'en_attente' ? '#f39c12' : '#e74c3c'}">
${getStatutText([Link])}
</span>
</div>
</div>
</div>
<!-- SECTION MODE DE PAIEMENT (CHOIX) -->
<div style="background: #2d2d2d; padding: 12px; border-radius: 4px; margin-bottom: 15px;">
<h4 style="color: #fff; margin-bottom: 10px; font-size: 0.9rem;">
<i class="fas fa-credit-card"></i> Mode de paiement
</h4>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap:
8px; margin-bottom: 10px;">
${[Link](mode => `
<button type="button"
class="mode-paiement-btn ${[Link] === [Link] ? 'selected' :
''}"
data-value="${[Link]}"
onclick="selectionnerModePaiementModal(this, '${[Link]}')"
style="background: ${[Link]}15; border: 2px solid ${[Link]
=== [Link] ? [Link] : '#444'};
padding: 10px; border-radius: 6px; cursor: pointer; transition: all 0.3s;
color: #fff; text-align: center;">
<i class="fas ${[Link]}" style="font-size: 1rem; margin-bottom: 5px; display: block;
color: ${[Link]};"></i>
<div style="font-size: 0.7rem;">${[Link]}</div>
</button>
`).join('')}
</div>
<small style="color: #888; font-size: 0.7rem;">
<i class="fas fa-info-circle"></i> Sélectionnez le mode de paiement utilisé pour cette
facture
</small>
</div>
<!-- SECTION COMMENTAIRE -->
<div style="background: #2d2d2d; padding: 12px; border-radius: 4px; margin-bottom: 15px;">
<h4 style="color: #fff; margin-bottom: 10px; font-size: 0.9rem;">
<i class="fas fa-sticky-note"></i> Commentaire
</h4>
<textarea id="commentaireFacture"
placeholder="Ajoutez un commentaire sur cette facture (max 1000 caractères)..."
style="width: 100%; height: 80px; background: #1a1a1a; border: 1px solid #444;
border-radius: 4px; color: #fff; padding: 8px; font-size: 0.8rem; resize: vertical;"
maxlength="1000">${[Link] || ''}</textarea>
<div style="display: flex; justify-content: space-between; margin-top: 5px;">
<small style="color: #888; font-size: 0.7rem;">
<i class="fas fa-info-circle"></i> Ce commentaire sera enregistré avec la facture
</small>
<small id="charCount" style="color: #888; font-size: 0.7rem;">
${([Link] || '').length}/1000
</small>
</div>
</div>
<!-- BOUTONS D'ACTION -->
<div style="display: flex; gap: 8px; justify-content: flex-end;">
<button onclick="fermerModal()"
style="background: #666; color: white; border: none; padding: 8px 16px; border-radius:
4px; cursor: pointer; font-size: 0.8rem;">
Annuler
</button>
<button onclick="sauvegarderModificationsFacture(${factureId})"
style="background: #3498db; color: white; border: none; padding: 8px 16px; border-
radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.8rem;"
id="btnSauvegarderModifications">
<i class="fas fa-save"></i> Sauvegarder
</button>
</div>
</div>
`;
// Ajouter le CSS pour les boutons sélectionnés
const style = [Link]('style');
[Link] = `
.[Link] {
transform: scale(1.05);
box-shadow: 0 0 0 2px ${[Link] ? '#3498db' : '#666'};
.mode-paiement-btn:hover {
transform: scale(1.05);
border-color: #3498db !important;
`;
[Link](style);
// Gestion du compteur de caractères
const textarea = [Link]('commentaireFacture');
const charCount = [Link]('charCount');
if (textarea && charCount) {
[Link]('input', function() {
const length = [Link];
[Link] = `${length}/1000`;
[Link] = length > 900 ? '#e74c3c' : length > 700 ? '#f39c12' : '#888';
});
[Link]('factureModal').[Link] = 'block';
// Stocker le mode de paiement sélectionné
[Link] = [Link];
}
// FONCTION POUR SÉLECTIONNER UN MODE DE PAIEMENT DANS LE MODAL
function selectionnerModePaiementModal(button, value) {
// Désélectionner tous les boutons
[Link]('.mode-paiement-btn').forEach(btn => {
[Link]('selected');
[Link] = '#444';
});
// Sélectionner le bouton cliqué
[Link]('selected');
// Stocker la sélection
[Link] = value;
[Link](' Mode de paiement sélectionné:', value);
// FONCTION POUR SAUVEGARDER LES MODIFICATIONS DE LA FACTURE
async function sauvegarderModificationsFacture(factureId) {
[Link](' Sauvegarde modifications facture:', factureId);
const btnSauvegarder = [Link]('btnSauvegarderModifications');
if (btnSauvegarder) {
[Link] = true;
[Link] = '<i class="fas fa-spinner fa-spin"></i> Sauvegarde...';
try {
const facture = [Link](f => [Link] === factureId);
if (!facture) {
throw new Error('Facture non trouvée');
// COLLECTE DES DONNÉES DU FORMULAIRE
const modePaiement = [Link];
const commentaire = [Link]('commentaireFacture')?.[Link]() || '';
const donneesMiseAJour = {};
// Ajouter mode de paiement seulement s'il a changé
if (modePaiement !== [Link]) {
donneesMiseAJour.mode_paiement = modePaiement;
// Ajouter commentaire seulement s'il a changé
if (commentaire !== ([Link] || '')) {
[Link] = commentaire;
// Vérifier s'il y a des modifications
if ([Link](donneesMiseAJour).length === 0) {
afficherNotification('Aucune modification à sauvegarder', 'info');
if (btnSauvegarder) {
[Link] = false;
[Link] = '<i class="fas fa-save"></i> Sauvegarder';
return;
[Link](' Données à envoyer:', donneesMiseAJour);
// APPEL API DE MISE À JOUR
const response = await fetch(`${API_BASE_URL_FACTURES}/factures/${factureId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: [Link](donneesMiseAJour)
});
[Link](' Statut réponse:', [Link]);
if (![Link]) {
const errorText = await [Link]();
const errorData = [Link](cleanApiResponse(errorText));
throw new Error([Link] || `Erreur serveur: ${[Link]}`);
const text = await [Link]();
const result = [Link](cleanApiResponse(text));
if ([Link]) {
// MISE À JOUR LOCALE DES DONNÉES
const factureIndex = [Link](f => [Link] === factureId);
if (factureIndex !== -1) {
allFactures[factureIndex] = {
...allFactures[factureIndex],
...(donneesMiseAJour.mode_paiement !== undefined && {
modePaiement: donneesMiseAJour.mode_paiement
}),
...([Link] !== undefined && {
commentaire: [Link]
})
};
afficherNotification(' Facture modifiée avec succès', 'success');
fermerModal();
afficherFactures();
mettreAJourStatistiques();
mettreAJourPagination();
} else {
throw new Error([Link] || 'Erreur lors de la mise à jour');
} catch (error) {
[Link](' Erreur sauvegarde:', error);
afficherNotification(` Erreur: ${[Link]}`, 'error');
if (btnSauvegarder) {
[Link] = false;
[Link] = '<i class="fas fa-save"></i> Sauvegarder';
// CONFIRMATION DE L'ANNULATION AVEC INTÉGRATION DANS LA COLONNE COMMENTAIRE
async function confirmerAnnulation(factureId) {
const motif = [Link]('motifAnnulation')?.[Link]();
if (!motif) {
afficherNotification('Veuillez saisir le motif de l\'annulation', 'error');
return;
const btnConfirmer = [Link]('btnConfirmerAnnulation');
if (btnConfirmer) {
[Link] = true;
[Link] = '<i class="fas fa-spinner fa-spin"></i> Traitement...';
try {
[Link](` Annulation de la facture ${factureId} avec motif: ${motif}`);
// INTÉGRATION DU MOTIF DANS LA COLONNE COMMENTAIRE
const commentaireAnnulation = `Facture annulée - Motif: ${motif}`;
// Appel API pour annuler la facture avec le motif dans la colonne commentaire
const response = await fetch(`${API_BASE_URL_FACTURES}/factures/${factureId}/annuler`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: [Link]({
motif_annulation: motif,
commentaire: commentaireAnnulation // INTÉGRATION DANS LA COLONNE
COMMENTAIRE
})
});
if (![Link]) {
// Si l'API n'est pas disponible, simuler l'annulation localement
throw new Error('API non disponible - simulation locale');
const text = await [Link]();
const result = [Link](cleanApiResponse(text));
if ([Link]) {
// Mettre à jour localement
const factureIndex = [Link](f => [Link] === factureId);
if (factureIndex !== -1) {
allFactures[factureIndex] = {
...allFactures[factureIndex],
statut: 'annulee',
motifAnnulation: motif,
commentaire: commentaireAnnulation, // MISE À JOUR LOCALE
datePaiement: null
};
afficherNotification(` Facture ${allFactures[factureIndex]?.numeroFacture} annulée avec
succès`, 'success');
} else {
throw new Error([Link] || 'Erreur lors de l\'annulation');
} catch (error) {
[Link](' Erreur annulation:', error);
// Simulation locale si l'API échoue
const factureIndex = [Link](f => [Link] === factureId);
if (factureIndex !== -1) {
const commentaireAnnulation = `Facture annulée - Motif: ${motif}`;
allFactures[factureIndex] = {
...allFactures[factureIndex],
statut: 'annulee',
motifAnnulation: motif,
commentaire: commentaireAnnulation, // MISE À JOUR LOCALE EN SIMULATION
datePaiement: null
};
afficherNotification(` Facture ${allFactures[factureIndex].numeroFacture} annulée (mode
simulation)`, 'success');
} else {
afficherNotification(' Erreur lors de l\'annulation', 'error');
return;
fermerModal();
afficherFactures();
mettreAJourStatistiques();
mettreAJourPagination();
// FONCTION POUR PAYER UNE FACTURE
function payerFacture(factureId) {
[Link](' Paiement facture:', factureId);
const facture = [Link](f => [Link] === factureId);
if (!facture) {
afficherNotification('Facture non trouvée', 'error');
return;
if ([Link] === 'annulee') {
afficherNotification('Impossible de payer une facture annulée', 'error');
return;
[Link]('factureDetails').innerHTML = `
<div style="max-width: 500px;">
<h3 style="color: #e74c3c; margin-bottom: 15px; border-bottom: 2px solid #e74c3c; padding-
bottom: 8px; font-size: 1.1rem;">
<i class="fas fa-credit-card"></i> Paiement Facture
</h3>
<div style="background: #2d2d2d; padding: 12px; border-radius: 4px; margin-bottom: 15px;">
<h4 style="color: #fff; margin-bottom: 10px; font-size: 0.9rem;">Détails de la facture</h4>
<div style="display: grid; gap: 6px; font-size: 0.8rem;">
<div><strong>N° Facture:</strong> ${[Link]}</div>
<div><strong>Client:</strong> ${[Link]}</div>
<div><strong>Date:</strong> ${formatDateTableau([Link])}</div>
<div><strong>Montant à payer:</strong> <span style="color: #e74c3c; font-weight: bold;
font-size: 1rem;">${formatMontantComplet([Link])}</span></div>
</div>
</div>
<div style="background: #2d2d2d; padding: 12px; border-radius: 4px; margin-bottom: 15px;">
<h4 style="color: #fff; margin-bottom: 10px; font-size: 0.9rem;">Mode de paiement</h4>
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px;">
<button onclick="selectionnerModePaiement('especes')"
style="background: #1a1a1a; border: 2px solid #444; padding: 12px; border-radius:
4px; color: #ccc; cursor: pointer; transition: all 0.3s; font-size: 0.8rem;"
id="btnEspeces">
<i class="fas fa-money-bill-wave" style="font-size: 1.2rem; margin-bottom: 4px; display:
block;"></i>
<div>Espèces</div>
</button>
<button onclick="selectionnerModePaiement('carte')"
style="background: #1a1a1a; border: 2px solid #444; padding: 12px; border-radius:
4px; color: #ccc; cursor: pointer; transition: all 0.3s; font-size: 0.8rem;"
id="btnCarte">
<i class="fas fa-credit-card" style="font-size: 1.2rem; margin-bottom: 4px; display:
block;"></i>
<div>Carte</div>
</button>
<button onclick="selectionnerModePaiement('mobile_money')"
style="background: #1a1a1a; border: 2px solid #444; padding: 12px; border-radius:
4px; color: #ccc; cursor: pointer; transition: all 0.3s; font-size: 0.8rem;"
id="btnMobileMoney">
<i class="fas fa-mobile-alt" style="font-size: 1.2rem; margin-bottom: 4px; display:
block;"></i>
<div>Mobile Money</div>
</button>
</div>
<div id="detailsPaiement" style="margin-top: 12px; display: none;"></div>
</div>
<div style="display: flex; gap: 8px; justify-content: flex-end;">
<button onclick="annulerPaiement()"
style="background: #666; color: white; border: none; padding: 8px 16px; border-radius:
4px; cursor: pointer; font-size: 0.8rem;">
Annuler
</button>
<button onclick="confirmerPaiement(${[Link]})"
style="background: #27ae60; color: white; border: none; padding: 8px 16px; border-
radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.8rem;"
id="btnConfirmerPaiement">
Confirmer le Paiement
</button>
</div>
</div>
`;
[Link]('factureModal').[Link] = 'block';
[Link] = factureId;
[Link] = null;
// FONCTIONS POUR LE PAIEMENT
function selectionnerModePaiement(mode) {
[Link](' Mode sélectionné:', mode);
[Link] = mode;
const buttons = ['btnEspeces', 'btnCarte', 'btnMobileMoney'];
[Link](btnId => {
const btn = [Link](btnId);
if (btn) [Link] = '#444';
});
const selectedBtn = [Link]('btn' + [Link](0).toUpperCase() +
[Link](1).replace('_', ''));
if (selectedBtn) [Link] = '#27ae60';
const detailsPaiement = [Link]('detailsPaiement');
[Link] = 'block';
let detailsHTML = '';
switch(mode) {
case 'especes':
detailsHTML = `
<div style="background: #1a1a1a; padding: 10px; border-radius: 4px;">
<h5 style="color: #fff; margin-bottom: 8px; font-size: 0.8rem;">Paiement en Espèces</h5>
<div style="color: #ccc; font-size: 0.8rem;">
<p> Le client paie en espèces. Merci de préparer la monnaie si nécessaire.</p>
</div>
</div>
`;
break;
case 'carte':
detailsHTML = `
<div style="background: #1a1a1a; padding: 10px; border-radius: 4px;">
<h5 style="color: #fff; margin-bottom: 8px; font-size: 0.8rem;">Paiement par Carte</h5>
<div style="color: #ccc; font-size: 0.8rem;">
<p> Insérez ou tapez la carte du client.</p>
</div>
</div>
`;
break;
case 'mobile_money':
detailsHTML = `
<div style="background: #1a1a1a; padding: 10px; border-radius: 4px;">
<h5 style="color: #fff; margin-bottom: 8px; font-size: 0.8rem;">Paiement par Mobile
Money</h5>
<div style="color: #ccc; font-size: 0.8rem;">
<p> Envoyez une demande de paiement au client</p>
</div>
</div>
`;
break;
[Link] = detailsHTML;
function cleanApiResponse(text) {
return [Link](/<!--.*?-->/g, '').trim();
async function confirmerPaiement(factureId) {
if (![Link]) {
afficherNotification('Veuillez sélectionner un mode de paiement', 'error');
return;
const btnConfirmer = [Link]('btnConfirmerPaiement');
if (btnConfirmer) {
[Link] = true;
[Link] = '<i class="fas fa-spinner fa-spin"></i> Traitement...';
}
try {
[Link](` Paiement facture ${factureId} via ${[Link]}`);
const facture = [Link](f => [Link] === factureId);
if (!facture) {
throw new Error('Facture non trouvée');
if ([Link] !== 'en_attente') {
throw new Error('Cette facture n\'est pas en attente de paiement');
const response = await fetch(`${API_BASE_URL_FACTURES}/factures/${factureId}/payer`, {
method: 'POST',
headers: {
'Accept': 'application/json'
});
[Link](' Statut réponse:', [Link]);
if (![Link]) {
const errorText = await [Link]();
const errorData = [Link](cleanApiResponse(errorText));
throw new Error([Link] || `Erreur serveur: ${[Link]}`);
const text = await [Link]();
const result = [Link](cleanApiResponse(text));
if ([Link]) {
const factureIndex = [Link](f => [Link] === factureId);
if (factureIndex !== -1) {
allFactures[factureIndex] = {
...allFactures[factureIndex],
statut: 'payee',
datePaiement: [Link].date_paiement || new Date().toISOString(),
modePaiement: [Link],
commentaire: `Facture payée - Mode: ${[Link]}` //
COMMENTAIRE DE PAIEMENT
};
const factureNumero = [Link]?.numero_facture || [Link];
afficherNotification(` Facture ${factureNumero} payée avec succès
(${[Link]})`, 'success');
fermerModal();
afficherFactures();
mettreAJourStatistiques();
mettreAJourPagination();
} else {
throw new Error([Link] || 'Erreur lors du paiement');
} catch (error) {
[Link](' Erreur paiement:', error);
afficherNotification(` Échec du paiement: ${[Link]}`, 'error');
if (btnConfirmer) {
[Link] = false;
[Link] = 'Confirmer le Paiement';
function annulerPaiement() {
[Link](' Paiement annulé');
fermerModal();
afficherNotification('Paiement annulé', 'info');
// AFFICHAGE DES DÉTAILS AVEC DESIGN COMPACT
async function afficherDetailsFacture(factureId) {
[Link](' Détails facture:', factureId);
try {
// Forcer la largeur à 6cm (environ 227 pixels)
const modalContent = [Link]('.modal-content');
[Link] = '227px';
[Link] = '227px';
[Link] = '227px';
const response = await fetch(`${API_BASE_URL_FACTURES}/factures/${factureId}`);
if (![Link]) {
throw new Error(`Erreur API: ${[Link]}`);
const text = await [Link]();
const cleanedText = cleanApiResponse(text);
const result = [Link](cleanedText);
if (![Link] || ![Link]) {
throw new Error('Données non disponibles');
const facture = [Link];
const ventes = [Link] || [];
let produitsHTML = '';
if ([Link] > 0) {
produitsHTML = `
<div style="margin-top: 10px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-
bottom: 8px;">
<h4 style="color: #fff; font-size: 0.75rem; margin: 0;">
<i class="fas fa-shopping-cart" style="font-size: 0.7rem;"></i> Produits
</h4>
<span style="background: #e74c3c; color: white; padding: 1px 6px; border-radius: 10px;
font-size: 0.6rem; font-weight: bold;">
${[Link]}
</span>
</div>
<div style="max-height: 150px; overflow-y: auto; border: 1px solid #2d2d2d; border-
radius: 4px;">
`;
let totalGeneral = 0;
[Link]((vente, index) => {
const produit = [Link] || {};
const nomProduit = [Link] || 'Produit inconnu';
const quantite = [Link] || 0;
const prixUnitaire = vente.prix_unitaire || 0;
const prixTotal = vente.prix_total || 0;
totalGeneral += prixTotal;
// Tronquer le nom du produit si trop long
const nomTronque = [Link] > 20 ? [Link](0, 20) + '...' :
nomProduit;
produitsHTML += `
<div style="padding: 6px; border-bottom: 1px solid #2d2d2d; ${index % 2 === 0 ?
'background: #1a1a1a;' : 'background: #222;'}">
<div style="font-weight: 600; color: #fff; font-size: 0.65rem; margin-bottom: 3px;"
title="${nomProduit}">
${nomTronque}
</div>
<div style="display: flex; justify-content: space-between; color: #888; font-size:
0.6rem;">
<span>${quantite} × ${formatMontantComplet(prixUnitaire)}</span>
<span style="color: #e74c3c; font-weight:
bold;">${formatMontantComplet(prixTotal)}</span>
</div>
</div>
`;
});
produitsHTML += `
</div>
<div style="background: linear-gradient(135deg, #e74c3c, #c0392b); padding: 8px; border-
radius: 4px; margin-top: 8px;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: white; font-weight: bold; font-size: 0.7rem;">TOTAL</span>
<span style="color: white; font-weight: bold; font-size:
0.8rem;">${formatMontantComplet(totalGeneral)}</span>
</div>
</div>
</div>
`;
} else {
produitsHTML = `
<div style="text-align: center; padding: 15px; color: #888; background: #1a1a1a; border-
radius: 4px; margin-top: 10px;">
<i class="fas fa-box-open" style="font-size: 1.5rem; margin-bottom: 8px; display: block;
color: #555;"></i>
<div style="font-size: 0.7rem;">Aucun produit</div>
</div>
`;
// Affichage du motif d'annulation et commentaire si présent
const commentaireHTML = [Link] ? `
<div style="background: #2d2d2d; padding: 10px; border-radius: 4px; margin-bottom: 10px;
border-left: 4px solid #3498db;">
<div style="display: flex; align-items: center; margin-bottom: 6px;">
<i class="fas fa-comment" style="color: #3498db; margin-right: 6px; font-size:
0.7rem;"></i>
<h4 style="color: #fff; margin: 0; font-size: 0.7rem;">Commentaire</h4>
</div>
<div style="color: #ccc; font-size: 0.65rem; font-style: italic;">
"${[Link]}"
</div>
</div>
` : '';
[Link]('factureDetails').innerHTML = `
<div style="max-width: 100%;">
<!-- EN-TÊTE COMPACTE -->
<div style="background: linear-gradient(135deg, #e74c3c, #c0392b); padding: 12px; border-
radius: 6px 6px 0 0; margin: -15px -15px 12px -15px;">
<div style="text-align: center;">
<h3 style="color: white; margin: 0 0 3px 0; font-size: 0.85rem; font-weight: bold;">
<i class="fas fa-file-invoice" style="font-size: 0.8rem;"></i> Détails
</h3>
<div style="color: rgba(255,255,255,0.9); font-size: 0.65rem; margin-bottom: 5px;">
${facture.numero_facture || 'N/A'}
</div>
<span class="statut-badge statut-${[Link]}" style="font-size: 0.6rem; padding:
2px 8px;">
${getStatutText([Link])}
</span>
</div>
</div>
${commentaireHTML}
<!-- INFORMATIONS CLIENT COMPACTES -->
<div style="background: #2d2d2d; padding: 10px; border-radius: 4px; margin-bottom:
10px;">
<div style="display: flex; align-items: center; margin-bottom: 6px;">
<i class="fas fa-building" style="color: #e74c3c; margin-right: 6px; font-size:
0.7rem;"></i>
<h4 style="color: #fff; margin: 0; font-size: 0.7rem;">Client</h4>
</div>
<div style="font-size: 0.65rem;">
<div style="color: #fff; font-weight: 500; margin-bottom: 4px; overflow: hidden; text-
overflow: ellipsis; white-space: nowrap;" title="${[Link]?.nom || 'Client inconnu'}">
${[Link]?.nom || 'Client inconnu'}
</div>
<div style="display: flex; justify-content: space-between; color: #ccc; margin-bottom:
3px;">
<span>Date:</span>
<span>${formatDateTableau(facture.date_creation)}</span>
</div>
${facture.date_paiement ? `
<div style="display: flex; justify-content: space-between; color: #27ae60; font-size:
0.6rem;">
<span>Payé le:</span>
<span>${formatDateTableau(facture.date_paiement)}</span>
</div>
` : ''}
</div>
</div>
<!-- PRODUITS COMPACTS -->
${produitsHTML}
<!-- BOUTON FERMER COMPACT -->
<div style="display: flex; justify-content: center; margin-top: 12px;">
<button onclick="fermerModal()"
style="background: #e74c3c; color: white; border: none; padding: 6px 15px; border-
radius: 3px; cursor: pointer; font-weight: 500; font-size: 0.65rem; transition: background 0.3s;"
onmouseover="[Link]='#c0392b'"
onmouseout="[Link]='#e74c3c'">
<i class="fas fa-times" style="font-size: 0.6rem;"></i> Fermer
</button>
</div>
</div>
`;
[Link]('factureModal').[Link] = 'block';
} catch (error) {
[Link](' Erreur chargement détails:', error);
afficherNotification(`Erreur: ${[Link]}`, 'error');
// FONCTIONS UTILITAIRES RESPONSIVES
function mettreAJourStatistiques() {
const total = [Link];
const payees = [Link](f => [Link] === 'payee').length;
const enAttente = [Link](f => [Link] === 'en_attente').length;
const annulees = [Link](f => [Link] === 'annulee').length;
const statsSection = [Link]('statsSection');
if (statsSection) {
[Link] = `
<div class="stat-item">
<span class="stat-number">${total}</span>
<span class="stat-label">Total</span>
</div>
<div class="stat-item" style="border-left-color: #27ae60;">
<span class="stat-number" style="color: #27ae60;">${payees}</span>
<span class="stat-label">Payées</span>
</div>
<div class="stat-item" style="border-left-color: #f39c12;">
<span class="stat-number" style="color: #f39c12;">${enAttente}</span>
<span class="stat-label">En attente</span>
</div>
<div class="stat-item" style="border-left-color: #e74c3c;">
<span class="stat-number" style="color: #e74c3c;">${annulees}</span>
<span class="stat-label">Annulées</span>
</div>
`;
function mettreAJourPagination() {
const totalPages = [Link]([Link] / ITEMS_PER_PAGE) || 1;
const paginationSection = [Link]('paginationSection');
if (paginationSection) {
[Link] = `
<button onclick="changerPage(${currentPage - 1})" ${currentPage <= 1 ? 'disabled' : ''}
class="page-btn">
<i class="fas fa-chevron-left"></i> Précédent
</button>
<span class="page-info">
Page ${currentPage} / ${totalPages}
</span>
<button onclick="changerPage(${currentPage + 1})" ${currentPage >= totalPages ? 'disabled' :
''}
class="page-btn">
Suivant <i class="fas fa-chevron-right"></i>
</button>
`;
// FORMATAGE DE DATE : JJ/MM/AAAA
function formatDateTableau(dateString) {
try {
const date = new Date(dateString);
const jour = [Link]().toString().padStart(2, '0');
const mois = ([Link]() + 1).toString().padStart(2, '0');
const annee = [Link]();
return `${jour}/${mois}/${annee}`;
} catch {
return dateString;
// FORMATAGE DES PRIX COMPLETS : 1 000, 10 000, 100 000
function formatMontantComplet(montant) {
return new [Link]('fr-FR').format(montant) + ' Ar';
function formatDateResponsive(dateString) {
return formatDateTableau(dateString);
function formatMontantResponsive(montant) {
return formatMontantComplet(montant);
function getStatutText(statut) {
const texts = {
'payee': 'Payée',
'en_attente': 'En attente',
'annulee': 'Annulée'
};
return texts[statut] || statut;
function filtrerFactures() {
currentPage = 1;
afficherFactures();
mettreAJourStatistiques();
mettreAJourPagination();
function changerPage(page) {
currentPage = page;
afficherFactures();
mettreAJourPagination();
function fermerModal() {
[Link]('factureModal').[Link] = 'none';
[Link] = null;
[Link] = null;
function exporterFactures() {
alert('Export PDF à implémenter');
function afficherNotification(message, type) {
[Link](` ${type}: ${message}`);
const notification = [Link]('div');
[Link] = `
position: fixed;
top: 20px;
right: 20px;
padding: 12px 16px;
border-radius: 4px;
color: white;
font-weight: bold;
z-index: 10000;
font-size: 0.8rem;
background: ${type === 'success' ? '#27ae60' : type === 'error' ? '#e74c3c' : '#f39c12'};
`;
[Link] = message;
[Link](notification);
setTimeout(() => {
[Link](notification);
}, 3000);
// DÉMARRAGE IMMÉDIAT
[Link](' Démarrage dans 100ms...');
setTimeout(demarrerApplication, 100);