import React, { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import "./NewExpense.css"; import { useContext } from "react"; import { langContext } from "../../context/LenguageContext"; import { AuthContext } from "../../context/AuthContext"; export default function NewExpense() { const { lang } = useContext(langContext); const { userData } = useContext(AuthContext); const navigate = useNavigate(); const isEditMode = false; const categoryRequiresProducts = (categoryId) => { return categoryId === 1 || categoryId === 10; }; const [form, setForm] = useState({ new_description: "", suppliers_id: "", new_request_date: "", new_payment_deadline: "", area: "", expense_cat: "", currency_id: "", new_tax: 1, subtotal: 0, iva: 0, ieps: 0, total: 0, needtoapprove: false, products: [], }); const [loading, setLoading] = useState(false); const [success, setSuccess] = useState(false); const [error, setError] = useState(""); // estados para datos de selects const [suppliers, setSuppliers] = useState([]); const [areas, setAreas] = useState([]); const [categories, setCategories] = useState([]); const [currencies, setCurrencies] = useState([]); const [taxes, setTaxes] = useState([]); const [listproducts, setListProducts] = useState([]); useEffect(() => { async function fetchSelectData() { try { const res = await fetch( import.meta.env.VITE_API_BASE_URL + "/expenses/getinfo", { method: "GET", headers: { "Content-Type": "application/json" }, } ); if (!res.ok) throw new Error(`Error fetching info: ${res.status}`); const data = await res.json(); // Supongo que data tiene algo como: // { suppliers: [...], areas: [...], categories: [...], currencies: [...] } setSuppliers(data.suppliers || []); setAreas(data.areas || []); setCategories(data.categories || []); setCurrencies(data.currencies || []); setTaxes(data.tax || []); } catch (err) { console.error("Error cargando metadata (getinfo):", err); } } // Helper para cargar opciones de selects const fetchOptions = (url, setter, mapFn) => { fetch(import.meta.env.VITE_API_BASE_URL + url) .then((res) => res.json()) .then((data) => { const items = data.data || data.currency || data.categoryex || data.approve || data.request; if (items) { setter(items.map(mapFn)); } }) .catch((err) => console.error(`Error fetching ${url}`, err)); }; fetchOptions("/products", (data) => { const sortedProducts = data.map(c => ({ id: c.id_product, name: c.name_product, })).sort((a, b) => a.name.localeCompare(b.name)); setListProducts(sortedProducts); }, (c) => c); fetchSelectData(); }, []); const handleChange = (e) => { const { name, type, checked, value } = e.target; // Crear una copia del formulario const updatedForm = { ...form, [name]: type === "checkbox" ? checked : value, }; // Inicializar valores por si están vacíos const subtotal = parseFloat(updatedForm.subtotal) || 0; const ieps = parseFloat(updatedForm.ieps) || 0; let ivaAmount = 0; if (name === "total" && !categoryRequiresProducts(Number(updatedForm.expense_cat))) { const totalValue = parseFloat(value) || 0; const iepsValue = parseFloat(updatedForm.ieps) || 0; const taxRate = parseFloat( taxes.find((t) => t.id === parseInt(updatedForm.new_tax))?.number || 0 ); if (taxRate > 0) { const subtotalBeforeIeps = totalValue - iepsValue; const calculatedSubtotal = subtotalBeforeIeps / (1 + taxRate); const calculatedIva = calculatedSubtotal * taxRate; updatedForm.subtotal = calculatedSubtotal; updatedForm.iva = calculatedIva; updatedForm.total = totalValue; } else { updatedForm.subtotal = totalValue - iepsValue; updatedForm.iva = 0; updatedForm.total = totalValue; } setForm({ ...updatedForm, subtotal: updatedForm.subtotal.toFixed(2), iva: updatedForm.iva.toFixed(2), total: updatedForm.total, }); return; } // Si hay productos, recalcular desde ellos if (updatedForm.products.length > 0) { updatedForm.subtotal = updatedForm.products.reduce( (acc, p) => acc + (parseFloat(p.subtotal) || 0), 0 ); ivaAmount = updatedForm.products.reduce( (acc, p) => acc + (parseFloat(p.iva) || 0), 0 ); console.log(ivaAmount); } else { const taxRate = parseFloat( taxes.find((t) => t.id === parseInt(updatedForm.new_tax))?.number || 0 ); console.log(taxRate); ivaAmount = subtotal * taxRate; } updatedForm.iva = ivaAmount; // Calcular total final (subtotal + IVA + IEPS) updatedForm.total = subtotal + ivaAmount + ieps; // Actualizar estado setForm({ ...updatedForm, iva: updatedForm.iva.toFixed(2), total: name === "total" ? updatedForm.total : updatedForm.total.toFixed(2), }); }; const handleCategoryChange = (e) => { const { name, value } = e.target; const categoryValue = Number(value) || 0; setForm((prev) => { const updatedForm = { ...prev, [name]: categoryValue }; if (categoryRequiresProducts(categoryValue)) { updatedForm.products = [ { id_product: null, quantity: 1, unit_cost: 0, id_tax: 1, subtotal: 0, iva: 0, }, ]; updatedForm.new_tax = ""; } else { // Si cambia a otra categoría, vaciamos los productos updatedForm.products = []; } return updatedForm; }); }; const handleProductChange = (index, field, value) => { const updatedProducts = [...form.products]; // Convertir a entero si es un campo numérico esperado const ieps = parseFloat(form.ieps) || 0; const processedValue = ["unit_cost"].includes(field) ? parseFloat(value) : ["quantity", "id_tax", "id_product"].includes(field) ? parseInt(value) : value; updatedProducts[index][field] = processedValue; if (field === "quantity" || field === "unit_cost" || field === "id_tax") { const qty = parseFloat(updatedProducts[index].quantity) || 0; const cost = parseFloat(updatedProducts[index].unit_cost) || 0; const taxValue = parseFloat( taxes.find((t) => t.id === parseInt(updatedProducts[index].id_tax)) ?.number || 0 ); updatedProducts[index].subtotal = qty * cost; updatedProducts[index].iva = qty * cost * taxValue; } // Recalcular el amount (suma de todos los productos) const subtotalAmount = updatedProducts.reduce( (acc, p) => acc + (parseFloat(p.subtotal) || 0), 0 ); // Recalcular el amount (suma de todos los productos) const ivaAmount = updatedProducts.reduce( (acc, p) => acc + (parseFloat(p.iva) || 0), 0 ); setError(null); setSuccess(false); setForm((prev) => ({ ...prev, products: updatedProducts, subtotal: subtotalAmount.toFixed(2), // redondeado a 2 decimales iva: ivaAmount.toFixed(2), // redondeado a 2 decimales total: (subtotalAmount + ivaAmount + ieps).toFixed(2), // Calcular total final (subtotal + IVA + IEPS) })); }; const handleAddProduct = () => { const ieps = parseFloat(form.ieps) || 0; // Primero, agregamos el nuevo producto const updatedProducts = [ ...form.products, { id_product: null, quantity: 1, unit_cost: 0, id_tax: "", subtotal: 0, iva: 0, }, ]; // Ahora sí, recalculamos después de agregarlo const subtotalAmount = updatedProducts.reduce( (acc, p) => acc + (parseFloat(p.subtotal) || 0), 0 ); const ivaAmount = updatedProducts.reduce( (acc, p) => acc + (parseFloat(p.iva) || 0), 0 ); setForm((prev) => ({ ...prev, products: updatedProducts, subtotal: subtotalAmount.toFixed(2), iva: ivaAmount.toFixed(2), total: (subtotalAmount + ivaAmount + ieps).toFixed(2), })); }; const handleRemoveProduct = (index) => { setForm((prev) => { const updated = [...prev.products]; updated.splice(index, 1); // Recalcular el amount (suma de todos los productos) const subtotalAmount = updated.reduce( (acc, p) => acc + (parseFloat(p.subtotal) || 0), 0 ); // Recalcular el amount (suma de todos los productos) const ivaAmount = updated.reduce( (acc, p) => acc + (parseFloat(p.iva) || 0), 0 ); const ieps = parseFloat(prev.ieps) || 0; return { ...prev, products: updated, subtotal: subtotalAmount.toFixed(2), // redondeado a 2 decimales iva: ivaAmount.toFixed(2), // redondeado a 2 decimales total: (subtotalAmount + ivaAmount + ieps).toFixed(2), // Calcular total final (subtotal + IVA + IEPS) }; }); }; const handleSubmit = async (e) => { e.preventDefault(); setLoading(true); setSuccess(false); setError(""); // ✅ Validar campos obligatorios antes de enviar if ( !form.suppliers_id || !form.area || !form.expense_cat || !form.currency_id || !form.total ) { setError( lang === "en" ? "Please fill in all required fields (Supplier, Area, Category, Currency, total)." : "Por favor llena todos los campos obligatorios (Proveedor, Área, Categoría, Moneda, Monto)." ); setLoading(false); return; } try { const payload = { new_description: form.new_description, suppliers_id: Number(form.suppliers_id), new_request_date: form.new_request_date, new_payment_deadline: form.new_payment_deadline, request_by: Number(userData.user_id), area: Number(form.area), expense_cat: Number(form.expense_cat), currency_id: Number(form.currency_id), needtoapprove: form.needtoapprove, products: form.products, new_subtotal: Number(form.subtotal), new_iva: Number(form.iva), new_ieps: Number(form.ieps), new_total: Number(form.total), }; // 🔍 LOGS para depurar el error 500 console.group("🧾 Envío de nuevo gasto"); console.log("Payload completo que se enviará al backend:", payload); console.log("Tipo de cada campo:"); Object.entries(payload).forEach(([k, v]) => console.log(`${k}:`, v, typeof v) ); console.groupEnd(); // Enviar petición console.log('payload', payload); const res = await fetch( import.meta.env.VITE_API_BASE_URL + "/expenses/newexpense", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), } ); console.log('res', res); const text = await res.text(); let data; try { data = JSON.parse(text); } catch { data = text; } // 🧩 Nuevo: Log para inspeccionar respuesta cruda console.group("📨 Respuesta del servidor"); console.log("Status:", res.status); console.log("Texto recibido:", text); console.groupEnd(); if (!res.ok) { console.error("Error response:", res.status, data); throw new Error(`Server error ${res.status}: ${JSON.stringify(data)}`); } setSuccess(true); // limpiar formulario setForm({ new_description: "", suppliers_id: "", new_request_date: "", new_payment_deadline: "", area: "", expense_cat: "", currency_id: "", subtotal: 0, needtoapprove: false, iva: 0, ieps: 0, total: 0, products: [], }); } catch (err) { console.error("❌ handleSubmit error:", err); setError(err.message || "Unexpected error"); } finally { setLoading(false); } }; return (