orderBy('year', 'desc')->get(); return view('settings.isr.index', compact('isrTables')); } /** * Crea nueva tabla por año */ public function store(Request $request) { $validator = Validator::make($request->all(), [ 'year' => 'required|integer|min:2000|max:2100|unique:isr_tables,year', ]); if ($validator->fails()) { return redirect()->route('settings.index', ['tab' => 'isr']) ->withErrors($validator) ->withInput(); } $isrTable = IsrTable::create([ 'year' => $request->input('year'), ]); return redirect()->route('settings.index', ['tab' => 'isr']) ->with('success', 'Tabla ISR para ' . $isrTable->year . ' creada. Ahora agrega los brackets.'); } /** * Formulario edición de brackets */ public function edit(IsrTable $isrTable) { $isrTable->load('brackets'); return view('settings.isr.edit', compact('isrTable')); } /** * Guarda brackets manuales */ public function updateBrackets(Request $request, IsrTable $isrTable) { $validator = Validator::make($request->all(), [ 'brackets' => 'required|array|min:1', 'brackets.*.lower_limit' => 'required|numeric|min:0', 'brackets.*.upper_limit' => 'nullable|numeric|min:0', 'brackets.*.fixed_fee' => 'required|numeric|min:0', 'brackets.*.rate' => 'required|numeric|min:0|max:100', ]); if ($validator->fails()) { return redirect()->route('settings.isr.edit', $isrTable) ->withErrors($validator) ->withInput(); } // Eliminar brackets existentes $isrTable->brackets()->delete(); // Crear nuevos brackets $bracketsData = $request->input('brackets'); $order = 0; // Ordenar brackets por lower_limit antes de guardar usort($bracketsData, function ($a, $b) { return floatval($a['lower_limit']) - floatval($b['lower_limit']); }); foreach ($bracketsData as $bracketData) { $isrTable->brackets()->create([ 'lower_limit' => floatval($bracketData['lower_limit']), 'upper_limit' => isset($bracketData['upper_limit']) && $bracketData['upper_limit'] !== '' ? floatval($bracketData['upper_limit']) : null, 'fixed_fee' => floatval($bracketData['fixed_fee']), 'rate' => floatval($bracketData['rate']), 'order' => $order++, ]); } return redirect()->route('settings.index', ['tab' => 'isr']) ->with('success', 'Brackets actualizados correctamente.'); } /** * Importa CSV */ public function uploadCsv(Request $request, IsrTable $isrTable) { $validator = Validator::make($request->all(), [ 'csv_file' => 'required|file|mimes:csv,txt', ]); if ($validator->fails()) { return redirect()->route('settings.isr.edit', $isrTable) ->withErrors($validator) ->withInput(); } $file = $request->file('csv_file'); $content = file_get_contents($file->getRealPath()); $lines = explode("\n", $content); $brackets = []; $firstLine = true; foreach ($lines as $line) { $line = trim($line); // Ignorar primera línea (encabezados vacíos) if ($firstLine) { $firstLine = false; continue; } if (empty($line)) { continue; } // Parsear línea CSV $values = str_getcsv($line); if (count($values) < 4) { continue; } $lowerLimit = $this->parseCsvValue($values[0]); $upperLimit = $this->parseCsvValue($values[1]); $fixedFee = $this->parseCsvValue($values[2]); $rate = $this->parseCsvValue($values[3]); // Ignorar si no hay lower_limit válido if ($lowerLimit === null || $lowerLimit < 0) { continue; } // Default null values to 0 $fixedFee = $fixedFee ?? 0; $rate = $rate ?? 0; $brackets[] = [ 'lower_limit' => $lowerLimit, 'upper_limit' => $upperLimit, 'fixed_fee' => $fixedFee, 'rate' => $rate, ]; } // Ordenar brackets por lower_limit usort($brackets, function ($a, $b) { return $a['lower_limit'] - $b['lower_limit']; }); if (empty($brackets)) { return redirect()->route('settings.index', ['tab' => 'isr']) ->with('error', 'No se encontraron brackets válidos en el archivo CSV.'); } // Eliminar brackets existentes $isrTable->brackets()->delete(); // Crear nuevos brackets $order = 0; foreach ($brackets as $bracket) { $isrTable->brackets()->create([ 'lower_limit' => $bracket['lower_limit'], 'upper_limit' => $bracket['upper_limit'], 'fixed_fee' => $bracket['fixed_fee'], 'rate' => $bracket['rate'], 'order' => $order++, ]); } return redirect()->route('settings.index', ['tab' => 'isr']) ->with('success', 'Brackets importados correctamente desde el CSV.'); } /** * Elimina tabla ISR */ public function destroy(IsrTable $isrTable) { $year = $isrTable->year; $isrTable->delete(); return redirect()->route('settings.index', ['tab' => 'isr']) ->with('success', 'Tabla ISR para ' . $year . ' eliminada.'); } /** * Convierte valor CSV como "3,537.15" a 3537.15 */ private function parseCsvValue(string $val): ?float { // Limpiar comillas y espacios $val = trim($val, '"\' '); // Manejar "En adelante" como null if (in_array(strtolower($val), ['en adelante', 'en adelante ', 'null', ''])) { return null; } // Eliminar comas de miles $val = str_replace(',', '', $val); // Convertir a número $num = floatval($val); // Permitir 0 como valor válido, pero null para valores negativos o no numéricos return is_numeric($val) ? $num : null; } }