Actualizar presupuestos.html
This commit is contained in:
@@ -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) {
|
||||
</div>
|
||||
`;
|
||||
}).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 = '<i data-lucide="save" class="w-4 h-4"></i> 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 = '<p class="text-emerald-600 font-bold text-center py-2 bg-white rounded-lg border border-emerald-100 shadow-sm">Libre: No tienes nada agendado este día.</p>';
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user