diff --git a/presupuestos.html b/presupuestos.html index 4a88244..e6399b9 100644 --- a/presupuestos.html +++ b/presupuestos.html @@ -364,14 +364,13 @@ } } catch (e) { showToast("Error de conexión"); } } -async function downloadPDF(id) { - // Buscamos el presupuesto en el array de la app móvil + + async function downloadPDF(id) { const budget = allBudgets.find(b => b.id === id); if(!budget) return showToast("❌ Error: Presupuesto no encontrado"); showToast("⏳ Generando PDF, espera unos segundos..."); - // Valores por defecto let empNombre = "IntegraRepara"; let empDni = "CIF/NIF no configurado"; let empAddress = "Dirección no configurada"; @@ -383,13 +382,11 @@ async function downloadPDF(id) { let empObs = null; try { - // Descargamos la configuración de tu empresa desde la API const confRes = await fetch(`${API_URL}/config/company`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const confData = await confRes.json(); if (confData.ok && confData.config) { empLogo = confData.config.company_logo || null; - if (confData.config.billing_settings) { const bSet = confData.config.billing_settings; empNombre = bSet.name || confData.config.full_name || empNombre; @@ -401,19 +398,15 @@ async function downloadPDF(id) { empIban = bSet.iban || null; empObs = bSet.obs || null; } else { - // Fallback por si no hay billing config guardada empNombre = confData.config.full_name || empNombre; } } } catch(e) { console.error("Error al cargar facturación", e); } - // Inyectamos los datos en el diseño document.getElementById('pdf-company-name').innerText = empNombre; document.getElementById('pdf-company-dni').innerText = empDni; document.getElementById('pdf-company-address').innerText = empAddress; - - const locText = [empZip, empCity, empState ? `(${empState})` : ''].filter(Boolean).join(' '); - document.getElementById('pdf-company-location').innerText = locText; + document.getElementById('pdf-company-location').innerText = [empZip, empCity, empState ? `(${empState})` : ''].filter(Boolean).join(' '); const logoImg = document.getElementById('pdf-company-logo'); const logoTxt = document.getElementById('pdf-company-name-fallback'); @@ -464,23 +457,22 @@ async function downloadPDF(id) { obsContainer.style.display = 'none'; } - // Generar PDF con html2pdf const wrapper = document.getElementById('pdf-wrapper'); wrapper.classList.remove('hidden'); wrapper.style.position = 'absolute'; - wrapper.style.left = '-9999px'; // Lo escondemos fuera de la pantalla del móvil + wrapper.style.left = '-9999px'; const element = document.getElementById('pdf-content'); const opt = { margin: 0, - filename: `Presupuesto_PRE${budget.id}_${budget.client_name.replace(/\s+/g, '_')}.pdf`, + filename: `Presupuesto_PRE${budget.id}_${(budget.client_name||'').replace(/\s+/g, '_')}.pdf`, image: { type: 'jpeg', quality: 1 }, html2canvas: { scale: 2, useCORS: true, logging: false }, jsPDF: { unit: 'in', format: 'a4', orientation: 'portrait' } }; try { - await new Promise(r => setTimeout(r, 500)); // Esperamos medio segundo para cargar logo + await new Promise(r => setTimeout(r, 500)); await html2pdf().set(opt).from(element).save(); showToast("✅ PDF Descargado con éxito"); } catch (error) { @@ -493,7 +485,7 @@ async function downloadPDF(id) { } } - // ------------------ RENDERIZAR LA LISTA EN PANTALLA ------------------ + // ------------------ RENDERIZAR LA LISTA EN PANTALLA ------------------ function renderBudgetsList(budgets) { const container = document.getElementById('budgetsList'); if(budgets.length === 0) { @@ -528,7 +520,6 @@ async function downloadPDF(id) { const d = bDate.toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: '2-digit' }); - // LÓGICA DE BOTONES PARA EL TÉCNICO let actionBtns = ''; if (b.status === 'pending' && !isExpired) { actionBtns = ` @@ -573,62 +564,45 @@ async function downloadPDF(id) { `; }).join(''); + lucide.createIcons(); + } - // ------------------ BUSCADOR DE CLIENTES AUTOMÁTICO ------------------ async function searchClientByPhone() { const phoneInput = document.getElementById('c_phone'); const phone = phoneInput.value.trim(); const loading = document.getElementById('phoneLoading'); - - // Si el teléfono tiene menos de 9 dígitos, no hacemos nada if (!phone || phone.length < 9) return; - loading.classList.remove('hidden'); // Mostramos el iconito de pensar - + loading.classList.remove('hidden'); try { const res = await fetch(`${API_URL}/clients/search?phone=${encodeURIComponent(phone)}`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const data = await res.json(); - if (data.ok && data.client) { - // Si encuentra al cliente, rellenamos los datos document.getElementById('c_name').value = data.client.full_name || ''; if (data.client.addresses && data.client.addresses.length > 0) { - // Cogemos la primera dirección que tenga guardada document.getElementById('c_address').value = data.client.addresses[0] || ''; } showToast("✅ Cliente encontrado y cargado"); } - } catch (e) { - console.error("Error buscando cliente", e); - } finally { - loading.classList.add('hidden'); // Ocultamos el iconito - } + } catch (e) { console.error("Error buscando cliente", e); } + finally { loading.classList.add('hidden'); } } - // ------------------ CREACIÓN DE PRESUPUESTO (ITEMS) ------------------ function addItem() { const conceptInput = document.getElementById('item_concept'); const qtyInput = document.getElementById('item_qty'); const priceInput = document.getElementById('item_price'); - const concept = conceptInput.value.trim(); const qty = parseFloat(qtyInput.value); const price = parseFloat(priceInput.value); - if(!concept || isNaN(qty) || isNaN(price)) { - showToast("Rellena concepto y precio."); - return; - } + if(!concept || isNaN(qty) || isNaN(price)) { showToast("Rellena concepto y precio."); return; } currentItems.push({ concept, qty, price, total: qty * price }); - - conceptInput.value = ''; - qtyInput.value = '1'; - priceInput.value = ''; + conceptInput.value = ''; qtyInput.value = '1'; priceInput.value = ''; conceptInput.focus(); - renderItems(); } @@ -658,19 +632,17 @@ async function downloadPDF(id) { function calcTotals() { let sub = 0; currentItems.forEach(i => sub += i.total); - let tax = sub * 0.21; // IVA 21% + let tax = sub * 0.21; let tot = sub + tax; document.getElementById('lbl_subtotal').innerText = `${sub.toFixed(2)} €`; document.getElementById('lbl_tax').innerText = `${tax.toFixed(2)} €`; document.getElementById('lbl_total').innerText = `${tot.toFixed(2)} €`; - return { sub, tax, tot }; } async function saveBudget() { if(currentItems.length === 0) { showToast("Añade al menos 1 artículo."); return; } - const cName = document.getElementById('c_name').value.trim(); const cPhone = document.getElementById('c_phone').value.trim(); const cAddress = document.getElementById('c_address').value.trim(); @@ -683,16 +655,7 @@ async function downloadPDF(id) { lucide.createIcons(); const totals = calcTotals(); - - const payload = { - client_name: cName, - client_phone: cPhone, - client_address: cAddress, - items: currentItems, - subtotal: totals.sub, - tax: totals.tax, - total: totals.tot - }; + const payload = { client_name: cName, client_phone: cPhone, client_address: cAddress, items: currentItems, subtotal: totals.sub, tax: totals.tax, total: totals.tot }; try { const res = await fetch(`${API_URL}/budgets`, { @@ -700,25 +663,20 @@ async function downloadPDF(id) { headers: { 'Content-Type': 'application/json', "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify(payload) }); - const data = await res.json(); if(data.ok) { showToast("¡Presupuesto Creado!"); hideCreateView(); fetchBudgets(); - } else { - showToast("Error al guardar."); - } - } catch(e) { - showToast("Fallo de conexión."); - } finally { + } else { showToast("Error al guardar."); } + } catch(e) { showToast("Fallo de conexión."); } + finally { btn.disabled = false; btn.innerHTML = ' Generar Presupuesto'; lucide.createIcons(); } } - // ------------------ SISTEMA DE CITA Y AGENDA EN TIEMPO REAL ------------------ function openAppointmentModal(id, clientName) { document.getElementById('apptBudgetId').value = id; document.getElementById('apptClientName').innerText = clientName || "Cliente"; @@ -729,7 +687,6 @@ async function downloadPDF(id) { document.getElementById('appointmentModal').classList.remove('hidden'); document.getElementById('appointmentModal').classList.add('flex'); setTimeout(() => document.getElementById('apptModalSheet').classList.remove('translate-y-full'), 10); - loadGuilds(); } @@ -757,7 +714,6 @@ async function downloadPDF(id) { async function checkAgendaForDate(dateStr) { const preview = document.getElementById('agendaPreview'); const list = document.getElementById('agendaList'); - if(!dateStr) { preview.classList.add('hidden'); return; } preview.classList.remove('hidden'); @@ -767,10 +723,8 @@ async function downloadPDF(id) { try { const res = await fetch(`${API_URL}/services`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const data = await res.json(); - if(data.ok && data.services) { const dayServices = data.services.filter(s => s.scheduled_date === dateStr && s.status !== 'archived'); - if(dayServices.length === 0) { list.innerHTML = '
Libre: No tienes nada agendado este día.
'; return; @@ -812,7 +766,6 @@ async function downloadPDF(id) { body: JSON.stringify(payload) }); const data = await res.json(); - if(data.ok) { showToast("✅ ¡Cita agendada con éxito!"); closeAppointmentModal();