diff --git a/contabilidad.html b/contabilidad.html index 630960f..c2e241d 100644 --- a/contabilidad.html +++ b/contabilidad.html @@ -443,6 +443,257 @@ } } + + // ========================================== + // LÓGICA DE PESTAÑAS + // ========================================== + function toggleTab(tab) { + const vFin = document.querySelector('.max-w-6xl > div:nth-child(3)'); // Los KPIs + const vSearch = document.querySelector('.max-w-6xl > div:nth-child(4)'); // Barra busqueda + const vTable = document.querySelector('.max-w-6xl > div:nth-child(5)'); // Tabla + const vPres = document.getElementById('view-presupuestos'); + + const tCobros = document.getElementById('tab-cobros'); + const tPres = document.getElementById('tab-presupuestos'); + + if (tab === 'cobros') { + vFin.classList.remove('hidden'); vSearch.classList.remove('hidden'); vTable.classList.remove('hidden'); + vPres.classList.add('hidden'); + tCobros.className = "px-6 py-3 font-black text-sm text-emerald-600 border-b-2 border-emerald-500 flex items-center gap-2 transition-colors"; + tPres.className = "px-6 py-3 font-bold text-sm text-slate-400 border-b-2 border-transparent hover:text-slate-600 transition-colors flex items-center gap-2"; + } else { + vFin.classList.add('hidden'); vSearch.classList.add('hidden'); vTable.classList.add('hidden'); + vPres.classList.remove('hidden'); + tPres.className = "px-6 py-3 font-black text-sm text-blue-600 border-b-2 border-blue-500 flex items-center gap-2 transition-colors"; + tCobros.className = "px-6 py-3 font-bold text-sm text-slate-400 border-b-2 border-transparent hover:text-slate-600 transition-colors flex items-center gap-2"; + loadBudgets(); + } + } + + // ========================================== + // LÓGICA DE PRESUPUESTOS Y ARTÍCULOS + // ========================================== + let myArticles = []; + let myBudgets = []; + + async function loadBudgets() { + try { + // Cargamos presupuestos y artículos a la vez + const [rB, rA] = await Promise.all([ + fetch(`${API_URL}/budgets`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }), + fetch(`${API_URL}/articles`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }) + ]); + const dataB = await rB.json(); + const dataA = await rA.json(); + + if (dataB.ok) myBudgets = dataB.budgets; + if (dataA.ok) myArticles = dataA.articles; + + renderBudgets(); + } catch(e) {} + } + + function renderBudgets() { + const list = document.getElementById('budgetsList'); + list.innerHTML = ""; + if(myBudgets.length === 0) { list.innerHTML = `
Sin presupuestos
`; return; } + + myBudgets.forEach(b => { + const date = new Date(b.created_at).toLocaleDateString('es-ES'); + + let bStatus = ''; + if(b.status === 'pending') bStatus = ` Pte. Resolver`; + if(b.status === 'rejected') bStatus = ` Rechazado`; + if(b.status === 'accepted') bStatus = ` Aceptado`; + if(b.status === 'converted') bStatus = ` Es Servicio`; + + let actions = ''; + if(b.status === 'pending') { + actions = ` + + + `; + } else if(b.status === 'accepted') { + actions = ``; + } + + list.innerHTML += ` +
+
+

${b.client_name}

+

${date} - 📞 ${b.client_phone}

+
+

${b.client_address}

+

${b.total}€

+
${bStatus} ${actions}
+
+ `; + }); + lucide.createIcons(); + } + + async function updateBudgetStatus(id, status) { + if(!confirm("¿Actualizar estado?")) return; + await fetch(`${API_URL}/budgets/${id}/status`, { method: 'PATCH', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({status}) }); + loadBudgets(); + } + + function openConvertModal(id) { + document.getElementById('convBudgetId').value = id; + document.getElementById('convertModal').classList.remove('hidden'); + } + + async function confirmConversion() { + const id = document.getElementById('convBudgetId').value; + const date = document.getElementById('convDate').value; + const time = document.getElementById('convTime').value; + if(!date || !time) return showToast("Debes elegir fecha y hora."); + + await fetch(`${API_URL}/budgets/${id}/convert`, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({date, time}) }); + document.getElementById('convertModal').classList.add('hidden'); + showToast("¡Convertido a Servicio con éxito!"); + loadBudgets(); + } + + // --- FORMULARIO NUEVO PRESUPUESTO --- + function openBudgetModal() { + document.getElementById('bPhone').value = ""; document.getElementById('bName').value = ""; document.getElementById('bAddress').value = ""; + document.getElementById('budgetLines').innerHTML = ""; + addBudgetLine(); // Crea una línea vacía + calcBudget(); + document.getElementById('budgetModal').classList.remove('hidden'); + } + + async function searchClientByPhone() { + const phone = document.getElementById('bPhone').value; + if(!phone || phone.length < 9) return; + const res = await fetch(`${API_URL}/clients/search?phone=${encodeURIComponent(phone)}`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const data = await res.json(); + if (data.client) { + document.getElementById('bName').value = data.client.full_name; + if(data.client.addresses && data.client.addresses.length > 0) document.getElementById('bAddress').value = data.client.addresses[0]; + showToast("Datos de cliente cargados"); + } + } + + function addBudgetLine() { + const lineId = Date.now(); + const artOptions = myArticles.map(a => ``).join(""); + + const html = ` +
+ + +
+ +
+ + +
+ +
+
+ `; + document.getElementById('budgetLines').insertAdjacentHTML('beforeend', html); + lucide.createIcons(); + } + + function applyArticleToLine(lineId, val) { + if(!val) return; + const [name, price] = val.split('|'); + const line = document.getElementById(`bl-${lineId}`); + line.querySelector('.b-concept').value = name; + line.querySelector('.b-price').value = price; + calcBudget(); + } + + function calcBudget() { + let sub = 0; + document.querySelectorAll('#budgetLines > div').forEach(line => { + const q = parseFloat(line.querySelector('.b-qty').value) || 0; + const p = parseFloat(line.querySelector('.b-price').value) || 0; + sub += (q * p); + }); + const tax = sub * 0.21; + const tot = sub + tax; + + document.getElementById('bSubtotal').innerText = sub.toFixed(2) + "€"; + document.getElementById('bTax').innerText = tax.toFixed(2) + "€"; + document.getElementById('bTotal').innerText = tot.toFixed(2) + "€"; + } + + async function saveBudget() { + const phone = document.getElementById('bPhone').value; + const name = document.getElementById('bName').value; + if(!name) return showToast("El nombre es obligatorio"); + + const items = []; + document.querySelectorAll('#budgetLines > div').forEach(line => { + const c = line.querySelector('.b-concept').value; + const q = parseFloat(line.querySelector('.b-qty').value) || 0; + const p = parseFloat(line.querySelector('.b-price').value) || 0; + if(c && q > 0) items.push({concept: c, qty: q, price: p}); + }); + + if(items.length === 0) return showToast("Añade al menos un concepto"); + + const sub = parseFloat(document.getElementById('bSubtotal').innerText); + const tax = parseFloat(document.getElementById('bTax').innerText); + const tot = parseFloat(document.getElementById('bTotal').innerText); + + const payload = { client_phone: phone, client_name: name, client_address: document.getElementById('bAddress').value, items, subtotal: sub, tax, total: tot }; + + await fetch(`${API_URL}/budgets`, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify(payload) }); + + document.getElementById('budgetModal').classList.add('hidden'); + showToast("Presupuesto guardado"); + loadBudgets(); + } + + // --- CATÁLOGO DE ARTÍCULOS --- + function openArticlesModal() { + renderArticles(); + document.getElementById('articlesModal').classList.remove('hidden'); + } + + function renderArticles() { + const list = document.getElementById('articlesList'); + list.innerHTML = myArticles.map(a => ` +
+ ${a.name} +
+ ${a.price}€ + +
+
+ `).join(""); + lucide.createIcons(); + } + + async function saveArticle() { + const name = document.getElementById('newArtName').value; + const price = document.getElementById('newArtPrice').value; + if(!name || !price) return; + await fetch(`${API_URL}/articles`, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({name, price}) }); + document.getElementById('newArtName').value = ""; document.getElementById('newArtPrice').value = ""; + await loadBudgets(); // Recarga y pinta + renderArticles(); + } + + async function editArticle(id, oldName, oldPrice) { + const n = prompt("Nuevo nombre:", oldName); + const p = prompt("Nuevo precio:", oldPrice); + if(n && p) { + await fetch(`${API_URL}/articles/${id}`, { method: 'PUT', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({name: n, price: p}) }); + await loadBudgets(); renderArticles(); + } + } + + + + function showToast(msg) { const t = document.getElementById('toast'); t.innerHTML = ` ${msg}`;