from = now()->format('Y-m-d'); } protected $rules = [ 'from' => 'required', 'to' => 'required', ]; protected $messages = [ 'from.required' => 'La data inizio è obbligatoria', 'to.required' => 'La data fine è obbligatoria', ]; public function resetFields() { $this->from = ''; $this->to = ''; } public function render() { return view('livewire.istat'); } public function export() { Log::info('Export ISTAT'); Log::info('From: ' . $this->from); Log::info('To: ' . $this->to); try { $items = \DB::table('fcf_reports_reports') ->leftjoin('fcf_users', 'fcf_reports_reports.created_by', '=', 'fcf_users.id') ->selectRaw('fcf_reports_reports.*, CONCAT(fcf_users.firstname," ",fcf_users.lastname) AS display_name') ->where(function ($query) { $query->where('fcf_reports_reports.incidente_con_feriti', '1') ->orWhere('fcf_reports_reports.incidente_mortale', '=', '1'); }) ->where('fcf_reports_reports.verificatosi_in_data', '>=', $this->from != '' ? $this->from : '2000-01-01') ->where('fcf_reports_reports.verificatosi_in_data', '<=', $this->to != '' ? $this->to : '2050-01-01') ->orderBy('fcf_reports_reports.verificatosi_in_data') ->get(); $txt = array(); $txt = array(); Log::info('Items: ' . count($items)); // Character counting variables $charCounts = []; $sectionCounts = []; foreach ($items as $item) { try { Log::info('Item: ' . $item->id); $report = \App\Models\Report::findOrFail($item->id); $item_data = ''; $sectionLength = 0; //1-10 try { $sectionStart = strlen($item_data); $town_code = $prov_code = '000'; $town = \App\Models\LocationTown::findOrFail($item->localita_uno); if ($town) { if ($town->istat_code) $town_code = $town->istat_code; $prov = \App\Models\LocationProvince::where('code', $town->prov)->first(); if ($prov && $prov->istat_code) $prov_code = $prov->istat_code; } $item_data .= date('y', strtotime($item->verificatosi_in_data)); $item_data .= date('m', strtotime($item->verificatosi_in_data)); $item_data .= str_pad($prov_code, 3, '0', STR_PAD_LEFT); $item_data .= str_pad($town_code, 3, '0', STR_PAD_LEFT); $item_data .= str_repeat(' ', 4); $item_data .= date('d', strtotime($item->verificatosi_in_data)); $item_data .= str_repeat(' ', 2); $item_data .= '4'; $item_data .= str_repeat(' ', 5); $item_data .= ' '; $sectionLength = strlen($item_data) - $sectionStart; $sectionCounts['Section 1-10'] = $sectionLength; Log::info('Item Data 1-10 Length: ' . $sectionLength); } catch (\Exception $e) { Log::error('Error in section 1-10 for item ' . $item->id . ': ' . $e->getMessage()); $item_data = str_pad($item_data, 20, ' '); } //11-20 try { $sectionStart = strlen($item_data); if ($item->localizzazione_incidente == Istat::LOCALIZZAZIONE_INCIDENTE_IN) { switch ($item->nomenclatura_strada) { case 2: $loc = 2; break; case 3: $loc = 0; break; case 4: $loc = 3; break; default: $loc = 1; } } elseif ($item->localizzazione_incidente == Istat::LOCALIZZAZIONE_INCIDENTE_OUT) { switch ($item->nomenclatura_strada) { case 2: $loc = 5; break; case 3: $loc = 9; break; case 4: $loc = 6; break; default: $loc = 4; } } else $loc = ' '; $tipo_strada = ' '; $types_saved = \App\Models\ReportTipoStrada::where('report_id', $report->id) ->pluck('tipo_strada_id') ->toArray(); if (!empty($types_saved)) { if (in_array(3, $types_saved) && in_array(1, $types_saved)) $tipo_strada = 1; elseif (in_array(3, $types_saved) && (in_array(11, $types_saved) || in_array(2, $types_saved))) $tipo_strada = 2; elseif (in_array(4, $types_saved)) $tipo_strada = 3; } $pav = ' '; if ($item->pavimentazione) switch ($item->pavimentazione) { case 5: case 6: $pav = 3; break; default: if (!$item->condizione_strada || $item->condizione_strada == 1) $pav = 1; else $pav = 2; } $int = ' '; switch ($item->particolarita_strada) { case 1: $int = '07'; break; case 2: $int = '01'; break; case 3: $int = '08'; break; case 4: $int = '02'; break; case 5: $int = '10'; break; case 6: case 10: $int = '09'; break; case 7: $int = '06'; break; case 12: $int = '04'; break; } $fon = ' '; if ($item->fondo_stradale) switch ($item->fondo_stradale) { case 1: $fon = 1; break; case 2: $fon = 2; break; case 3: $fon = 4; break; case 4: $fon = 5; break; case 5: $fon = 3; break; } $seg = ' '; if ($item->segnaletica) switch ($item->segnaletica) { case Istat::SEGNALETICA_ASSENTE: $seg = 1; break; case Istat::SEGNALETICA_VERTICALE: $seg = 2; break; case Istat::SEGNALETICA_ORIZZONTALE: $seg = 3; break; case Istat::SEGNALETICA_VERTICALE_ORIZZONTALE: $seg = 4; break; case Istat::SEGNALETICA_TEMPORANEA_CANTIERE: $seg = 5; break; } $met = ' '; if ($item->condizioni_atmosferiche) switch ($item->condizioni_atmosferiche) { case 1: $met = 1; break; case 2: $met = 2; break; case 3: $met = 3; break; case 4: $met = 2; break; case 5: $met = 7; break; case 6: $met = 1; break; case 7: $met = 4; break; case 8: $met = 5; break; case 9: $met = 6; break; } $item_data .= str_pad($loc, 1, ' ', STR_PAD_RIGHT); $item_data .= str_pad($item->nomenclatura_strada_numero, 3, ' ', STR_PAD_RIGHT); $item_data .= str_repeat(' ', 3); $item_data .= str_repeat(' ', 2); $item_data .= str_pad($tipo_strada, 1, ' ', STR_PAD_RIGHT); $item_data .= str_pad($pav, 1, ' ', STR_PAD_RIGHT); $item_data .= str_pad($int, 2, ' ', STR_PAD_RIGHT); $item_data .= str_pad($fon, 1, ' ', STR_PAD_RIGHT); $item_data .= str_pad($seg, 1, ' ', STR_PAD_RIGHT); $item_data .= str_pad($met, 1, ' ', STR_PAD_RIGHT); $sectionLength = strlen($item_data) - $sectionStart; $sectionCounts['Section 11-20'] = $sectionLength; Log::info('Item Data 11-20 Length: ' . $sectionLength); } catch (\Exception $e) { Log::error('Error in section 11-20 for item ' . $item->id . ': ' . $e->getMessage()); $item_data = str_pad($item_data, 20, ' '); } try { //21-30 $sectionStart = strlen($item_data); $tipo_urto = str_repeat(' ', 2); $types_saved = \App\Models\ReportTipoUrto::where('report_id', $report->id)->pluck('tipo_urto_id') ->toArray(); if ($types_saved) { if (in_array(1, $types_saved)) $tipo_urto = '01'; elseif (in_array(2, $types_saved)) $tipo_urto = '02'; elseif (in_array(3, $types_saved) || in_array(26, $types_saved) || in_array(27, $types_saved)) $tipo_urto = '04'; elseif (in_array(5, $types_saved)) $tipo_urto = '03'; elseif (in_array(7, $types_saved) || in_array(12, $types_saved)) $tipo_urto = '05'; elseif (in_array(4, $types_saved) || in_array(21, $types_saved)) $tipo_urto = '07'; elseif (in_array(8, $types_saved)) $tipo_urto = '06'; elseif (in_array(6, $types_saved) || in_array(19, $types_saved) || in_array(14, $types_saved)) $tipo_urto = '08'; elseif (in_array(9, $types_saved) || in_array(18, $types_saved)) $tipo_urto = '10'; elseif (in_array(25, $types_saved) || in_array(22, $types_saved) || in_array(13, $types_saved)) $tipo_urto = '12'; } $item_data .= $tipo_urto; $v_data = array( 'tipo' => '', 'circostanze' => '', 'veicolo' => '', 'conducente_passeggeri' => '', 'pedoni' => '', 'cittadinanza_conducente' => '' ); $deceduti_24h = array(); $deceduti_30gg = array(); $feriti = array(); $data_veicoli = \App\Models\ReportDataVeicoli::where('report_id', $report->id)->get()->toArray(); for ($i = 0; $i < 3; $i++) { $data_veicolo = isset($data_veicoli[$i]) ? $data_veicoli[$i] : false; $stato_psico_fisico = str_repeat(' ', 2); if ($data_veicolo) { if ($data_veicolo["veicoli"] > 0) { $veicolo = \App\Models\Vehicle::findOrFail($data_veicolo["veicoli"]); $v_tipo = str_repeat(' ', 2); switch ($veicolo->tipo_id) { case 1: switch ($veicolo->destinazione_uso) { case \App\Models\Vehicle::DESTINAZIONE_USO_PRIVATO: $v_tipo = '01'; break; case \App\Models\Vehicle::DESTINAZIONE_USO_PUBBLICO: $v_tipo = '03'; break; case \App\Models\Vehicle::DESTINAZIONE_USO_SOCCORSO_POLIZIA: $v_tipo = '04'; break; default: $v_tipo = '01'; break; } break; case 17: $v_tipo = '02'; break; case 7: if ($veicolo->destinazione_uso == \App\Models\Vehicle::DESTINAZIONE_USO_SERVIZO_PUBBLICO_DI_LINEA) $v_tipo = '05'; else $v_tipo = '06'; break; case 16: $v_tipo = '08'; break; case 14: case 20: $v_tipo = '09'; break; case 18: $v_tipo = '10'; break; case 8: $v_tipo = '13'; break; case 6: $v_tipo = '14'; break; case 3: $v_tipo = '15'; break; case 5: $v_tipo = '17'; break; case 19: $v_tipo = '18'; break; case 4: $v_tipo = '21'; break; } $v_data['tipo'] .= $v_tipo; $v_data['veicolo'] .= $veicolo->foreign_country ? str_repeat(' ', 8) : str_pad(substr($veicolo->targa, 0, 8), 8, ' ', STR_PAD_RIGHT); $v_data['veicolo'] .= $veicolo->foreign_country ? str_pad($veicolo->getCartaCircolazioneRilasciataDaDiValueAttribute(), 3, ' ', STR_PAD_RIGHT) : str_repeat(' ', 3); $time = strtotime($veicolo->carta_circolazione_rilasciata_il); $v_data['veicolo'] .= str_pad($time && $time > 0 ? date('y', $time) : '', 2, ' ', STR_PAD_RIGHT); $v_data['veicolo'] .= str_repeat(' ', 5); } if ($data_veicolo["alcool_test"] && $data_veicolo["alcool_test_esito"] == Istat::ESITO_POSITIVO) $stato_psico_fisico = '90'; elseif ($data_veicolo["drug_test"] && $data_veicolo["drug_test_esito"] == Istat::ESITO_POSITIVO) $stato_psico_fisico = '94'; $conducente = Anagrafica::findOrFail($data_veicolo["conducenti"]); $eta_conducente = '00'; if ($conducente->birth_date != '' && $report->verificatosi_in_data != '') { $eta_conducente = 0; if ($eta_conducente > 100 || $eta_conducente < 0) $eta_conducente = 0; $eta_conducente = str_pad($eta_conducente, 2, '0', STR_PAD_LEFT); } $v_data['conducente_passeggeri'] .= $eta_conducente; $v_data['conducente_passeggeri'] .= $conducente->gender == 'F' ? '2' : '1'; $details = array( 'firstname' => $conducente->firstname, 'lastname' => $conducente->lastname, 'ospedale' => $data_veicolo["infortunato_ospedale"], ); switch ($data_veicolo["infortunato"]) { case Istat::INFORTUNATO_DECEDUTO_30GG: $infortunato = 4; $deceduti_30gg[] = $details; break; case Istat::INFORTUNATO_DECEDUTO_24H: $infortunato = 3; $deceduti_24h[] = $details; break; case Istat::INFORTUNATO_FERITO: $infortunato = 2; $feriti[] = $details; break; default: $infortunato = 1; break; } $v_data['conducente_passeggeri'] .= $infortunato; switch ($data_veicolo["conducente_patente_categoria"]) { case Istat::PATENTE_CATEGORIA_AM: $patente = '0'; break; case Istat::PATENTE_CATEGORIA_A1: case Istat::PATENTE_CATEGORIA_A1B: case Istat::PATENTE_CATEGORIA_A2: case Istat::PATENTE_CATEGORIA_A: $patente = '1'; break; case Istat::PATENTE_CATEGORIA_AB: case Istat::PATENTE_CATEGORIA_B1: case Istat::PATENTE_CATEGORIA_B: $patente = '2'; break; case Istat::PATENTE_CATEGORIA_C1: case Istat::PATENTE_CATEGORIA_C: $patente = '3'; break; case Istat::PATENTE_CATEGORIA_D1: $patente = '4'; break; case Istat::PATENTE_CATEGORIA_BE: case Istat::PATENTE_CATEGORIA_C1E: case Istat::PATENTE_CATEGORIA_CE: case Istat::PATENTE_CATEGORIA_D1E: case Istat::PATENTE_CATEGORIA_DE: case Istat::PATENTE_CATEGORIA_E: $patente = '5'; break; default: $patente = '9'; break; } $v_data['conducente_passeggeri'] .= $patente; $patente_rilasciata_il = $data_veicolo["conducente_patente_rilasciata_il"]; $v_data['conducente_passeggeri'] .= $patente_rilasciata_il ? date('y', strtotime($patente_rilasciata_il)) : str_repeat(' ', 2); $v_data['conducente_passeggeri'] .= str_repeat(' ', 4); $datiPasseggeri = \App\Models\ReportDataPasseggeri::where('progressive', $i)->where('report_id', $report->id)->get()->toArray(); for ($j = 0; $j < 4; $j++) { $passeggero_data = str_repeat(' ', 4); if (isset($datiPasseggeri[$j])) { $passeggero = $datiPasseggeri[$j]["passeggero"]; if ($passeggero) { $passeggero_infortunato = $datiPasseggeri[$j]["infortunato"]; if ($passeggero_infortunato != Istat::INFORTUNATO_INCOLUME) { $passeggero_anagrafica = Anagrafica::findOrFail($passeggero); $eta_passeggero = '00'; if ($passeggero_anagrafica->birth_date && $report->verificatosi_in_data) { $eta_passeggero = 0; DateHelper::calculateAge($passeggero_anagrafica->birth_date, $report->verificatosi_in_data); if ($eta_passeggero == 0) $eta_passeggero = 1; elseif ($eta_passeggero > 100 || $eta_passeggero < 0) $eta_passeggero = 0; $eta_passeggero = str_pad($eta_passeggero, 2, '0', STR_PAD_LEFT); } $passeggero_data = sprintf( '%s%s%s', $passeggero_infortunato == Istat::INFORTUNATO_FERITO ? '2' : '1', $eta_passeggero, $passeggero_anagrafica->gender == 'F' ? '4' : '3' ); $details = array( 'firstname' => $passeggero_anagrafica->firstname, 'lastname' => $passeggero_anagrafica->lastname, 'ospedale' => $datiPasseggeri[$j]["infortunato_ospedale"] ); switch ($passeggero_infortunato) { case Istat::INFORTUNATO_DECEDUTO_30GG: $deceduti_30gg[] = $details; break; case Istat::INFORTUNATO_DECEDUTO_24H: $deceduti_24h[] = $details; break; case Istat::INFORTUNATO_FERITO: $feriti[] = $details; break; } } } } $v_data['conducente_passeggeri'] .= $passeggero_data; } $v_data['conducente_passeggeri'] .= str_repeat(' ', 8); $v_data['cittadinanza_conducente'] .= $conducente->nazionalita == 0 ? '1' : '2'; $country_id = 106; if ($conducente->nazionalita != 0) $country_id = $conducente->nazione_straniera; if (!$country_id) $v_data['cittadinanza_conducente'] .= str_repeat(' ', 33); else { $country = \App\Models\LocationCountry::findOrFail($country_id); $v_data['cittadinanza_conducente'] .= str_pad($country->istat_code, 3, ' ', STR_PAD_RIGHT); $v_data['cittadinanza_conducente'] .= str_pad(preg_replace('[^A-Z ]', '', strtoupper($country->name)), 30, ' ', STR_PAD_RIGHT); } $countryDataLen = strlen($v_data['cittadinanza_conducente']); $expectedCountryDataLen = 34; if ($countryDataLen < $expectedCountryDataLen) { $v_data['cittadinanza_conducente'] .= str_repeat(' ', $expectedCountryDataLen - $countryDataLen); } } else { $v_data['tipo'] .= str_repeat(' ', 2); $v_data['veicolo'] .= str_repeat(' ', 18); $v_data['conducente_passeggeri'] .= str_repeat(' ', 35); $v_data['pedoni'] .= str_repeat(' ', 24); $v_data['cittadinanza_conducente'] .= str_repeat(' ', 34); } if ($i < 2) { $v_data['circostanze'] .= '00'; $v_data['circostanze'] .= str_repeat(' ', 2); $v_data['circostanze'] .= $stato_psico_fisico; } } $p_data = ''; $datiPedoni = ReportDataPedoni::where('report_id', $report->id)->get()->toArray(); for ($j = 0; $j < 4; $j++) { $pedone_data = str_repeat(' ', 6); if (isset($datiPedoni[$j])) { $pedone = $datiPedoni[$j]["pedoni"]; if ($pedone) { $pedone_infortunato = $infortunato; if ($pedone_infortunato != Istat::INFORTUNATO_INCOLUME) { $pedone_anagrafica = new Anagrafica([$pedone]); $eta_pedone = '00'; if (DateHelper::filterDate($pedone_anagrafica->get('birth_date')) && DateHelper::filterDate($report->get('verificatosi_in_data'))) { $eta_pedone = DateHelper::calculateAge($pedone_anagrafica->get('birth_date'), $report->get('verificatosi_in_data')); if ($eta_pedone == 0) $eta_pedone = 1; elseif ($eta_pedone > 100 || $eta_pedone < 0) $eta_pedone = 0; $eta_pedone = str_pad($eta_pedone, 2, '0', STR_PAD_LEFT); } if ($pedone_infortunato == Istat::INFORTUNATO_FERITO) $pedone_data = sprintf( '%s%s%s', str_repeat(' ', 3), $pedone_anagrafica->get('gender') == 'F' ? '4' : '3', $eta_pedone ); else $pedone_data = sprintf( '%s%s%s', $pedone_anagrafica->get('gender') == 'F' ? '2' : '1', $eta_pedone, str_repeat(' ', 3) ); $details = array( 'firstname' => $pedone_anagrafica->get('firstname'), 'lastname' => $pedone_anagrafica->get('lastname'), 'ospedale' => ReportDataPedoni::where('report_id', $report->id) ->where('progressive', $j) ->value('infortunato_ospedale') ); switch ($pedone_infortunato) { case Istat::INFORTUNATO_DECEDUTO_30GG: $deceduti_30gg[] = $details; break; case Istat::INFORTUNATO_DECEDUTO_24H: $deceduti_24h[] = $details; break; case Istat::INFORTUNATO_FERITO: $feriti[] = $details; break; } } } } $p_data .= $pedone_data; } $v_data['tipo'] = str_pad($v_data['tipo'], 6, ' ', STR_PAD_RIGHT); $item_data .= $v_data['tipo']; $item_data .= str_repeat(' ', 24); $v_data['circostanze'] = str_pad($v_data['circostanze'], 12, ' ', STR_PAD_RIGHT); $item_data .= $v_data['circostanze']; $v_data['veicolo'] = str_pad($v_data['veicolo'], 54, ' ', STR_PAD_RIGHT); $item_data .= $v_data['veicolo']; $v_data['conducente_passeggeri'] = str_pad($v_data['conducente_passeggeri'], 105, ' ', STR_PAD_RIGHT); $item_data .= $v_data['conducente_passeggeri']; $p_data = str_pad($p_data, 24, ' ', STR_PAD_RIGHT); $item_data .= $p_data; $item_data .= str_repeat(' ', 10); $item_data .= str_pad(count($deceduti_24h), 2, '0', STR_PAD_LEFT); $item_data .= str_pad(count($deceduti_30gg), 2, '0', STR_PAD_LEFT); $item_data .= str_pad(count($feriti), 2, '0', STR_PAD_LEFT); $item_data .= str_repeat(' ', 9); $sectionLength = strlen($item_data) - $sectionStart; $sectionCounts['Section 21-30'] = $sectionLength; } catch (\Exception $e) { Log::error('Error in section 21-30 for item ' . $item->id . ': ' . $e->getMessage()); } try { //31-40 $sectionStart = strlen($item_data); $stradario_due = \App\Models\Stradario::find($report->localita_due); $location_description = $stradario_due ? (($stradario_due['TOPONIMO'] ?? '') . ' ' . ($stradario_due['DESCRIZIONE'] ?? '')) : ''; if ($report->intersezione_con) { $intersezione = \App\Models\Stradario::find($report->intersezione_con); if ($intersezione) { $intersezione_toponimo = $intersezione['TOPONIMO']; $intersezione_address = $intersezione['DESCRIZIONE']; $full_intersection = $intersezione_toponimo . ' ' . $intersezione_address; $location_description .= ' INTERSEZIONE CON ' . $full_intersection; } } if ($report->prossimita_civico) { $location_description .= ' PROSSIMITA CIVICO ' . $report->prossimita_civico; } if ($report->prossimita_chilometro) { $location_description .= ' PROSSIMITA CHILOMETRO ' . $report->prossimita_chilometro; } $item_data .= str_pad(substr(preg_replace('/[^A-Za-z0-9 ]/', '', strtoupper($location_description)), 0, 57), 57, ' '); $item_data .= str_repeat(' ', 100); $deceduti = array_merge($deceduti_24h, $deceduti_30gg); for ($i = 0; $i < 4; $i++) { $deceduto = isset($deceduti[$i]) ? $deceduti[$i] : null; $deceduto_data = str_repeat(' ', 60); if ($deceduto) { $deceduto_data = sprintf( '%s%s', str_pad(substr($deceduto['firstname'], 0, 30), 30, ' '), str_pad(substr($deceduto['lastname'], 0, 30), 30, ' ') ); } $item_data .= $deceduto_data; } for ($i = 0; $i < 8; $i++) { $ferito = isset($feriti[$i]) ? $feriti[$i] : null; $ferito_data = str_repeat(' ', 90); if ($ferito) { $ferito_data = sprintf( '%s%s%s', str_pad(substr($ferito['firstname'], 0, 30), 30, ' '), str_pad(substr($ferito['lastname'], 0, 30), 30, ' '), str_pad(substr($ferito['ospedale'], 0, 30), 30, ' ') ); } $item_data .= $ferito_data; } $item_data .= str_repeat(' ', 10); $item_data .= str_repeat(' ', 102); $item_data .= str_repeat(' ', 8); $ora = $report->verificatosi_in_data_ora ?? ''; $ora = $ora === '' ? '25' : str_pad(substr($ora, 0, 2), 2, '0', STR_PAD_LEFT); $item_data .= $ora; $minuti = (int) ($report->verificatosi_in_data_minuti ?? 0); if ($ora == '25') { $minuti = ' '; } $item_data .= str_pad(substr($minuti, 0, 2), 2, '0', STR_PAD_LEFT); $item_data .= str_repeat(' ', 30); $item_data .= str_repeat('0', 7); $item_data .= str_repeat(' ', 15); $item_data .= str_repeat(' ', 4); $item_data .= str_repeat(' ', 100); $town = \App\Models\LocationTown::find( $report->localita_uno); $town_title = $town ? $town['istat_code'] : ''; $item_data .= str_pad(substr($town_title, 0, 40), 40, ' '); $item_data .= str_repeat(' ', 40); $item_data .= str_repeat(' ', 10); $item_data .= $v_data['cittadinanza_conducente']; $sectionLength = strlen($item_data) - $sectionStart; $sectionCounts['Section 31-40'] = $sectionLength; Log::info('Item Data 31-40 Length: ' . $sectionLength); $totalLength = strlen($item_data); $charCounts[] = $totalLength; Log::info('Total item data length: ' . $totalLength); } catch (\Exception $e) { Log::error('Error in section 31-40 for item ' . $item->id . ': ' . $e->getMessage()); } $item_data = str_pad($item_data, 1939, ' '); $txt[] = $item_data; } catch (\Exception $e) { Log::error('Major error processing item ' . $item->id . ': ' . $e->getMessage()); continue; } } $txt_data = implode("\r\n", $txt); return response()->streamDownload(function () use ($txt_data) { echo $txt_data; }, 'istat_' . $this->from . "_" . $this->to . '.txt'); } catch (\Exception $e) { Log::error('Fatal error in export: ' . $e->getMessage()); return response()->json(['error' => 'Export failed: ' . $e->getMessage()], 500); } } }