Spaces:
Running
Running
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Facturation Horaires</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-in-out; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .invoice-paper { | |
| background: linear-gradient(to bottom right, #fff, #f9f9f9); | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.1); | |
| border: 1px solid #e5e7eb; | |
| } | |
| .watermark { | |
| position: absolute; | |
| opacity: 0.05; | |
| font-size: 120px; | |
| font-weight: bold; | |
| color: #3b82f6; | |
| transform: rotate(-30deg); | |
| pointer-events: none; | |
| user-select: none; | |
| z-index: 0; | |
| } | |
| #clientList::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| #clientList::-webkit-scrollbar-track { | |
| background: #f1f1f1; | |
| border-radius: 10px; | |
| } | |
| #clientList::-webkit-scrollbar-thumb { | |
| background: #c1c1c1; | |
| border-radius: 10px; | |
| } | |
| #clientList::-webkit-scrollbar-thumb:hover { | |
| background: #a8a8a8; | |
| } | |
| .print-only { | |
| display: none; | |
| } | |
| @media print { | |
| body * { | |
| visibility: hidden; | |
| } | |
| #invoice-print, #invoice-print * { | |
| visibility: visible; | |
| } | |
| #invoice-print { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| width: 100%; | |
| margin: 0; | |
| padding: 20px; | |
| box-shadow: none; | |
| border: none; | |
| } | |
| .print-only { | |
| display: block; | |
| } | |
| .no-print { | |
| display: none ; | |
| } | |
| .watermark { | |
| opacity: 0.1; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8 no-print"> | |
| <header class="mb-10 text-center"> | |
| <h1 class="text-4xl font-bold text-blue-600 mb-2">Facturation Horaires</h1> | |
| <p class="text-gray-600">Générez facilement des factures pour vos clients</p> | |
| </header> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
| <!-- Section Clients --> | |
| <div class="lg:col-span-1 bg-white rounded-xl shadow-md p-6"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800">Clients</h2> | |
| <button id="addClientBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center transition-colors"> | |
| <i class="fas fa-plus mr-2"></i> Ajouter | |
| </button> | |
| </div> | |
| <!-- Liste des clients --> | |
| <div id="clientList" class="space-y-3 max-h-96 overflow-y-auto pr-2"> | |
| <!-- Les clients seront ajoutés ici dynamiquement --> | |
| <div class="text-center py-4 text-gray-500"> | |
| <i class="fas fa-users text-2xl mb-2"></i> | |
| <p>Aucun client enregistré</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section Facturation --> | |
| <div class="lg:col-span-2 space-y-8"> | |
| <!-- Formulaire de facturation --> | |
| <div class="bg-white rounded-xl shadow-md p-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800 mb-6">Créer une facture</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div> | |
| <label for="selectClient" class="block text-sm font-medium text-gray-700 mb-1">Client *</label> | |
| <select id="selectClient" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="" selected disabled>Sélectionnez un client</option> | |
| </select> | |
| <p id="clientError" class="text-red-500 text-xs mt-1 hidden">Veuillez sélectionner un client</p> | |
| </div> | |
| <div> | |
| <label for="hoursWorked" class="block text-sm font-medium text-gray-700 mb-1">Heures travaillées *</label> | |
| <input type="number" id="hoursWorked" min="0" step="0.5" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="Ex: 5.5"> | |
| <p id="hoursError" class="text-red-500 text-xs mt-1 hidden">Veuillez entrer un nombre valide</p> | |
| </div> | |
| <div> | |
| <label for="workDate" class="block text-sm font-medium text-gray-700 mb-1">Date de travail *</label> | |
| <input type="date" id="workDate" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| <div> | |
| <label for="invoiceDate" class="block text-sm font-medium text-gray-700 mb-1">Date de facture *</label> | |
| <input type="date" id="invoiceDate" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| </div> | |
| <div class="mt-6"> | |
| <label for="workDescription" class="block text-sm font-medium text-gray-700 mb-1">Description du travail</label> | |
| <textarea id="workDescription" rows="3" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="Détaillez les travaux effectués..."></textarea> | |
| </div> | |
| <div class="mt-6 flex justify-end"> | |
| <button id="generateInvoiceBtn" class="bg-green-500 hover:bg-green-600 text-white px-6 py-2 rounded-lg flex items-center transition-colors"> | |
| <i class="fas fa-file-invoice-dollar mr-2"></i> Générer la facture | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Aperçu de la facture --> | |
| <div id="invoicePreviewContainer" class="hidden bg-white rounded-xl shadow-md p-6 fade-in"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800">Aperçu de la facture</h2> | |
| <div class="flex space-x-2"> | |
| <button id="downloadPdfBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded-lg flex items-center transition-colors"> | |
| <i class="fas fa-file-pdf mr-2"></i> PDF | |
| </button> | |
| <button id="printInvoiceBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center transition-colors"> | |
| <i class="fas fa-print mr-2"></i> Imprimer | |
| </button> | |
| </div> | |
| </div> | |
| <div id="invoicePreview" class="invoice-paper p-8 rounded-lg relative overflow-hidden"> | |
| <div class="watermark">FACTURE</div> | |
| <!-- Le contenu de la facture sera généré ici --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Modal Ajout Client --> | |
| <div id="clientModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
| <div class="bg-white rounded-xl shadow-xl p-6 w-full max-w-md fade-in"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h3 class="text-xl font-semibold text-gray-800">Ajouter un client</h3> | |
| <button id="closeModalBtn" class="text-gray-500 hover:text-gray-700 transition-colors"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <form id="clientForm" class="space-y-4"> | |
| <div> | |
| <label for="clientName" class="block text-sm font-medium text-gray-700 mb-1">Nom complet *</label> | |
| <input type="text" id="clientName" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="Jean Dupont"> | |
| <p id="nameError" class="text-red-500 text-xs mt-1 hidden">Ce champ est obligatoire</p> | |
| </div> | |
| <div> | |
| <label for="clientEmail" class="block text-sm font-medium text-gray-700 mb-1">Email</label> | |
| <input type="email" id="clientEmail" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="client@exemple.com"> | |
| </div> | |
| <div> | |
| <label for="clientPhone" class="block text-sm font-medium text-gray-700 mb-1">Téléphone</label> | |
| <input type="tel" id="clientPhone" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="06 12 34 56 78"> | |
| </div> | |
| <div> | |
| <label for="clientAddress" class="block text-sm font-medium text-gray-700 mb-1">Adresse</label> | |
| <textarea id="clientAddress" rows="2" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="123 Rue des Exemples, 75000 Paris"></textarea> | |
| </div> | |
| <div> | |
| <label for="hourlyRate" class="block text-sm font-medium text-gray-700 mb-1">Taux horaire (€) *</label> | |
| <input type="number" id="hourlyRate" min="0" step="0.01" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="50.00"> | |
| <p id="rateError" class="text-red-500 text-xs mt-1 hidden">Veuillez entrer un taux valide</p> | |
| </div> | |
| <div class="pt-4 flex justify-end space-x-3"> | |
| <button type="button" id="cancelClientBtn" class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-100 transition-colors">Annuler</button> | |
| <button type="submit" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg transition-colors">Enregistrer</button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| <!-- Contenu pour l'impression --> | |
| <div id="invoice-print" class="hidden"></div> | |
| <script> | |
| // Stockage des clients | |
| let clients = JSON.parse(localStorage.getItem('clients')) || []; | |
| // Éléments du DOM | |
| const clientList = document.getElementById('clientList'); | |
| const selectClient = document.getElementById('selectClient'); | |
| const addClientBtn = document.getElementById('addClientBtn'); | |
| const clientModal = document.getElementById('clientModal'); | |
| const closeModalBtn = document.getElementById('closeModalBtn'); | |
| const cancelClientBtn = document.getElementById('cancelClientBtn'); | |
| const clientForm = document.getElementById('clientForm'); | |
| const generateInvoiceBtn = document.getElementById('generateInvoiceBtn'); | |
| const invoicePreviewContainer = document.getElementById('invoicePreviewContainer'); | |
| const invoicePreview = document.getElementById('invoicePreview'); | |
| const printInvoiceBtn = document.getElementById('printInvoiceBtn'); | |
| const downloadPdfBtn = document.getElementById('downloadPdfBtn'); | |
| const invoicePrint = document.getElementById('invoice-print'); | |
| // Messages d'erreur | |
| const clientError = document.getElementById('clientError'); | |
| const hoursError = document.getElementById('hoursError'); | |
| const nameError = document.getElementById('nameError'); | |
| const rateError = document.getElementById('rateError'); | |
| // Initialisation | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Définir les dates par défaut | |
| const today = new Date().toISOString().split('T')[0]; | |
| document.getElementById('workDate').value = today; | |
| document.getElementById('invoiceDate').value = today; | |
| // Charger les clients | |
| loadClients(); | |
| // Événements | |
| addClientBtn.addEventListener('click', showClientModal); | |
| closeModalBtn.addEventListener('click', hideClientModal); | |
| cancelClientBtn.addEventListener('click', hideClientModal); | |
| clientForm.addEventListener('submit', saveClient); | |
| generateInvoiceBtn.addEventListener('click', generateInvoice); | |
| printInvoiceBtn.addEventListener('click', printInvoice); | |
| downloadPdfBtn.addEventListener('click', downloadPdf); | |
| // Empêcher la fermeture du modal en cliquant à l'extérieur | |
| clientModal.addEventListener('click', function(e) { | |
| if (e.target === clientModal) { | |
| hideClientModal(); | |
| } | |
| }); | |
| }); | |
| // Fonctions | |
| function loadClients() { | |
| clientList.innerHTML = ''; | |
| selectClient.innerHTML = '<option value="" selected disabled>Sélectionnez un client</option>'; | |
| if (clients.length === 0) { | |
| clientList.innerHTML = ` | |
| <div class="text-center py-4 text-gray-500"> | |
| <i class="fas fa-users text-2xl mb-2"></i> | |
| <p>Aucun client enregistré</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| clients.forEach((client, index) => { | |
| // Ajouter à la liste des clients | |
| const clientElement = document.createElement('div'); | |
| clientElement.className = 'bg-gray-50 hover:bg-gray-100 p-4 rounded-lg cursor-pointer transition-colors'; | |
| clientElement.innerHTML = ` | |
| <div class="flex justify-between items-center"> | |
| <div> | |
| <h3 class="font-medium text-gray-800">${client.name}</h3> | |
| <p class="text-sm text-gray-500">${client.address ? client.address.split(',')[0] : ''}</p> | |
| </div> | |
| <div class="text-right"> | |
| <p class="font-semibold text-blue-600">${parseFloat(client.hourlyRate).toFixed(2)} €/h</p> | |
| <button class="delete-client text-red-500 hover:text-red-700 text-sm" data-index="${index}"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| clientList.appendChild(clientElement); | |
| // Ajouter au select | |
| const option = document.createElement('option'); | |
| option.value = index; | |
| option.textContent = `${client.name} (${parseFloat(client.hourlyRate).toFixed(2)} €/h)`; | |
| selectClient.appendChild(option); | |
| }); | |
| // Gérer la suppression des clients | |
| document.querySelectorAll('.delete-client').forEach(btn => { | |
| btn.addEventListener('click', function(e) { | |
| e.stopPropagation(); | |
| const index = parseInt(this.getAttribute('data-index')); | |
| deleteClient(index); | |
| }); | |
| }); | |
| // Gérer la sélection d'un client dans la liste | |
| document.querySelectorAll('#clientList > div').forEach((el, index) => { | |
| if (index < clients.length) { | |
| el.addEventListener('click', () => { | |
| selectClient.value = index; | |
| clientError.classList.add('hidden'); | |
| }); | |
| } | |
| }); | |
| } | |
| function showClientModal() { | |
| clientModal.classList.remove('hidden'); | |
| document.getElementById('clientName').focus(); | |
| clientForm.reset(); | |
| nameError.classList.add('hidden'); | |
| rateError.classList.add('hidden'); | |
| } | |
| function hideClientModal() { | |
| clientModal.classList.add('hidden'); | |
| } | |
| function saveClient(e) { | |
| e.preventDefault(); | |
| const name = document.getElementById('clientName').value.trim(); | |
| const hourlyRate = document.getElementById('hourlyRate').value; | |
| // Validation | |
| let isValid = true; | |
| if (!name) { | |
| nameError.classList.remove('hidden'); | |
| isValid = false; | |
| } else { | |
| nameError.classList.add('hidden'); | |
| } | |
| if (!hourlyRate || isNaN(hourlyRate) || parseFloat(hourlyRate) <= 0) { | |
| rateError.classList.remove('hidden'); | |
| isValid = false; | |
| } else { | |
| rateError.classList.add('hidden'); | |
| } | |
| if (!isValid) return; | |
| const client = { | |
| name: name, | |
| email: document.getElementById('clientEmail').value.trim(), | |
| phone: document.getElementById('clientPhone').value.trim(), | |
| address: document.getElementById('clientAddress').value.trim(), | |
| hourlyRate: parseFloat(hourlyRate).toFixed(2) | |
| }; | |
| clients.push(client); | |
| localStorage.setItem('clients', JSON.stringify(clients)); | |
| hideClientModal(); | |
| loadClients(); | |
| // Sélectionner le nouveau client | |
| selectClient.value = clients.length - 1; | |
| clientError.classList.add('hidden'); | |
| } | |
| function deleteClient(index) { | |
| if (confirm('Voulez-vous vraiment supprimer ce client ?')) { | |
| clients.splice(index, 1); | |
| localStorage.setItem('clients', JSON.stringify(clients)); | |
| loadClients(); | |
| // Masquer l'aperçu si le client supprimé était sélectionné | |
| if (selectClient.value === index.toString()) { | |
| selectClient.value = ''; | |
| invoicePreviewContainer.classList.add('hidden'); | |
| } | |
| } | |
| } | |
| function generateInvoice() { | |
| const clientIndex = selectClient.value; | |
| const hoursWorked = parseFloat(document.getElementById('hoursWorked').value); | |
| const workDate = document.getElementById('workDate').value; | |
| const invoiceDate = document.getElementById('invoiceDate').value; | |
| const workDescription = document.getElementById('workDescription').value.trim(); | |
| // Validation | |
| let isValid = true; | |
| if (clientIndex === '' || clientIndex === null) { | |
| clientError.classList.remove('hidden'); | |
| isValid = false; | |
| } else { | |
| clientError.classList.add('hidden'); | |
| } | |
| if (isNaN(hoursWorked) || hoursWorked <= 0) { | |
| hoursError.classList.remove('hidden'); | |
| isValid = false; | |
| } else { | |
| hoursError.classList.add('hidden'); | |
| } | |
| if (!workDate) { | |
| alert('Veuillez sélectionner une date de travail'); | |
| return; | |
| } | |
| if (!invoiceDate) { | |
| alert('Veuillez sélectionner une date de facture'); | |
| return; | |
| } | |
| if (!isValid) return; | |
| const client = clients[clientIndex]; | |
| const totalAmount = (hoursWorked * parseFloat(client.hourlyRate)).toFixed(2); | |
| // Formater les dates | |
| const formattedWorkDate = formatDate(workDate); | |
| const formattedInvoiceDate = formatDate(invoiceDate); | |
| // Générer la facture | |
| const invoiceContent = ` | |
| <div class="watermark">FACTURE</div> | |
| <div class="flex justify-between items-start mb-12"> | |
| <div> | |
| <h1 class="text-3xl font-bold text-blue-600 mb-1">FACTURE</h1> | |
| <p class="text-gray-500">N° ${generateInvoiceNumber()}</p> | |
| </div> | |
| <div class="text-right"> | |
| <p class="text-gray-700">Date: <span class="font-medium">${formattedInvoiceDate}</span></p> | |
| <p class="text-gray-700">Échéance: <span class="font-medium">${formattedInvoiceDate}</span></p> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12"> | |
| <div> | |
| <h2 class="text-lg font-semibold text-gray-800 mb-2">De:</h2> | |
| <p class="font-bold">Votre Entreprise</p> | |
| <p>123 Rue de l'Entreprise</p> | |
| <p>75000 Paris, France</p> | |
| <p class="mt-2">SIRET: 123 456 789 00010</p> | |
| <p>TVA: FR12 345678901</p> | |
| </div> | |
| <div> | |
| <h2 class="text-lg font-semibold text-gray-800 mb-2">À:</h2> | |
| <p class="font-bold">${client.name}</p> | |
| ${client.address ? `<p>${client.address.replace(/\n/g, '<br>')}</p>` : ''} | |
| ${client.email ? `<p class="mt-2">Email: ${client.email}</p>` : ''} | |
| ${client.phone ? `<p>Téléphone: ${client.phone}</p>` : ''} | |
| </div> | |
| </div> | |
| <div class="mb-8"> | |
| <h2 class="text-lg font-semibold text-gray-800 mb-4">Détails de la facture</h2> | |
| <div class="overflow-x-auto"> | |
| <table class="w-full border-collapse"> | |
| <thead> | |
| <tr class="bg-gray-100"> | |
| <th class="text-left py-3 px-4 font-semibold text-gray-700 border-b">Description</th> | |
| <th class="text-right py-3 px-4 font-semibold text-gray-700 border-b">Taux horaire</th> | |
| <th class="text-right py-3 px-4 font-semibold text-gray-700 border-b">Heures</th> | |
| <th class="text-right py-3 px-4 font-semibold text-gray-700 border-b">Montant</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td class="py-3 px-4 border-b text-gray-700"> | |
| ${workDescription || 'Prestation de services'} | |
| <p class="text-sm text-gray-500 mt-1">Date: ${formattedWorkDate}</p> | |
| </td> | |
| <td class="py-3 px-4 border-b text-right text-gray-700">${parseFloat(client.hourlyRate).toFixed(2)} €</td> | |
| <td class="py-3 px-4 border-b text-right text-gray-700">${hoursWorked.toFixed(1)}</td> | |
| <td class="py-3 px-4 border-b text-right font-medium text-gray-800">${totalAmount} €</td> | |
| </tr> | |
| </tbody> | |
| <tfoot> | |
| <tr> | |
| <td colspan="3" class="py-3 px-4 text-right font-semibold text-gray-700">Total HT</td> | |
| <td class="py-3 px-4 text-right font-bold text-gray-800">${totalAmount} €</td> | |
| </tr> | |
| <tr> | |
| <td colspan="3" class="py-3 px-4 text-right font-semibold text-gray-700">TVA (0%)</td> | |
| <td class="py-3 px-4 text-right font-bold text-gray-800">0,00 €</td> | |
| </tr> | |
| <tr> | |
| <td colspan="3" class="py-3 px-4 text-right font-semibold text-gray-700 border-t-2 border-gray-200">Total TTC</td> | |
| <td class="py-3 px-4 text-right font-bold text-gray-800 border-t-2 border-gray-200">${totalAmount} €</td> | |
| </tr> | |
| </tfoot> | |
| </table> | |
| </div> | |
| </div> | |
| <div class="text-sm text-gray-500 pt-8 border-t border-gray-200"> | |
| <p class="font-semibold mb-2">Conditions de paiement:</p> | |
| <p>Paiement à réception de la facture par virement bancaire.</p> | |
| <p class="mt-4">IBAN: FR76 1234 5678 9012 3456 7890 123</p> | |
| <p>BIC: ABCDEFGH123</p> | |
| <p class="mt-4">En cas de retard de paiement, des pénalités de 1,5% par mois seront appliquées.</p> | |
| </div> | |
| <div class="print-only mt-12 pt-4 border-t border-gray-200 text-center text-sm text-gray-500"> | |
| <p>Merci pour votre confiance</p> | |
| </div> | |
| `; | |
| invoicePreview.innerHTML = invoiceContent; | |
| invoicePrint.innerHTML = invoiceContent; | |
| invoicePreviewContainer.classList.remove('hidden'); | |
| invoicePreviewContainer.scrollIntoView({ behavior: 'smooth' }); | |
| } | |
| function printInvoice() { | |
| window.print(); | |
| } | |
| function downloadPdf() { | |
| alert("Fonctionnalité PDF à implémenter. Pour l'instant, vous pouvez utiliser l'impression et sélectionner 'Enregistrer au format PDF' comme imprimante."); | |
| } | |
| function formatDate(dateString) { | |
| if (!dateString) return ''; | |
| const options = { day: '2-digit', month: '2-digit', year: 'numeric' }; | |
| return new Date(dateString).toLocaleDateString('fr-FR', options); | |
| } | |
| function generateInvoiceNumber() { | |
| const now = new Date(); | |
| const year = now.getFullYear(); | |
| const month = String(now.getMonth() + 1).padStart(2, '0'); | |
| const day = String(now.getDate()).padStart(2, '0'); | |
| const random = Math.floor(Math.random() * 1000); | |
| return `FACT-${year}${month}${day}-${random}`; | |
| } | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Flynf/facturation" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |