"Archivo CSV no encontrado: {$csvFilePath}"]; } $handle = fopen($csvFilePath, 'r'); $batchSize = 5000; $data = []; $rowCount = 0; $insertedCount = 0; while (($row = fgetcsv($handle, 1000, ";")) !== false) { $record = []; foreach ($columns as $index => $column) { $value = $row[$index] ?? null; // Aplicar CAST según el tipo de dato if ($value === '') { $record[$column] = null; } elseif (self::isDateColumn($column)) { $record[$column] = self::formatDate($value); } elseif (is_numeric($value)) { $record[$column] = strpos($value, '.') !== false ? (float) $value : (int) $value; } else { $record[$column] = trim($value); } } $data[] = $record; $rowCount++; if (count($data) >= $batchSize) { self::insertOrUpdate($tableName, $data, $updateExisting); $insertedCount += count($data); $data = []; } } fclose($handle); if (!empty($data)) { self::insertOrUpdate($tableName, $data, $updateExisting); $insertedCount += count($data); } return [ "inserted" => $insertedCount, "total_rows" => $rowCount ]; } /** * Inserta o actualiza datos en la tabla. */ private static function insertOrUpdate(string $tableName, array $data, bool $updateExisting) { if (empty($data)) { echo "⚠️ Datos vacíos para {$tableName}\n"; return; } foreach ($data as &$record) { foreach ($record as $key => $value) { if ($value === '?') { // Si hay un '?', lo convertimos en NULL $record[$key] = null; } } } unset($record); try { $primaryKeys = self::getPrimaryKeys($tableName); $updateFields = array_diff(array_keys($data[0]), $primaryKeys); if ($updateExisting && !empty($updateFields)) { DB::table($tableName)->upsert($data, $primaryKeys, $updateFields); echo "✅ Upsert ejecutado en {$tableName} (" . count($data) . " filas)\n"; } else { DB::table($tableName)->insert($data); echo "✅ Insert ejecutado en {$tableName} (" . count($data) . " filas)\n"; } } catch (\Exception $e) { Log::error("🚨 Error en {$tableName}: " . $e->getMessage()); echo "❌ ERROR en {$tableName}: {$e->getMessage()}\n"; die(); } } /** * **Detecta si una columna es de tipo fecha.** */ private static function isDateColumn(string $columnName): bool { $dateFields = ['fecha_inicio_vigencia', 'fecha_fin_vigencia']; return in_array($columnName, $dateFields); } /** * **Convierte fechas de diferentes formatos a `YYYY-MM-DD`.** */ private static function formatDate($value) { if (!$value || strtolower($value) === 'null') { return null; } try { if (is_numeric($value) && $value > 10000) { return Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($value))->format('Y-m-d'); } if (preg_match('/^\d{2}\/\d{2}\/\d{4}$/', $value)) { return Carbon::createFromFormat('d/m/Y', $value)->format('Y-m-d'); } return Carbon::parse($value)->format('Y-m-d'); } catch (\Exception $e) { return null; } } /** * **Obtiene las claves primarias de la tabla.** */ private static function getPrimaryKeys(string $tableName): array { $primaryKeys = [ 'sat_forma_pago' => ['c_forma_pago'], 'sat_moneda' => ['c_moneda'], 'sat_codigo_postal' => ['c_codigo_postal'], 'sat_regimen_fiscal' => ['c_regimen_fiscal'], 'sat_pais' => ['c_pais'], 'sat_uso_cfdi' => ['c_uso_cfdi'], 'sat_clave_prod_serv' => ['c_clave_prod_serv'], 'sat_clave_unidad' => ['c_clave_unidad'], 'sat_aduana' => ['c_aduana'], 'sat_colonia' => ['c_colonia', 'c_codigo_postal'], 'sat_estado' => ['c_estado', 'c_pais'], 'sat_localidad' => ['c_localidad', 'c_estado'], 'sat_municipio' => ['c_municipio', 'c_estado'], 'sat_banco' => ['c_banco'], 'sat_deduccion' => ['c_deduccion'], 'sat_percepcion' => ['c_percepcion'], 'sat_regimen_contratacion' => ['c_regimen_contratacion'], ]; return $primaryKeys[$tableName] ?? ['id']; } }