, * meta: array{header_row:int, delimiter:string, parsed_rows:int, skipped_rows:int} * } */ public function parse(string $content): array { $content = str_replace(["\r\n", "\r"], "\n", $content); $lines = explode("\n", $content); $delimiter = ';'; $headerIndex = null; $scanMax = min(count($lines), 300); for ($i = 0; $i < $scanMax; $i++) { $line = trim((string) $lines[$i]); if ($line === '') { continue; } if ( stripos($line, 'Data contabile') !== false && stripos($line, 'Accrediti') !== false && stripos($line, 'Addebiti') !== false ) { $headerIndex = $i; break; } } if ($headerIndex === null) { throw new \RuntimeException('Formato CSV non riconosciuto: header "Data contabile" non trovato'); } $header = str_getcsv((string) $lines[$headerIndex], $delimiter); $header = array_map(fn($h) => $this->normHeader((string) $h), $header); $hmap = []; foreach ($header as $idx => $h) { if ($h === '') { continue; } if (!array_key_exists($h, $hmap)) { $hmap[$h] = (int) $idx; } } $idxData = $hmap['datacontabile'] ?? null; $idxValuta = $hmap['datavaluta'] ?? null; $idxDescr = $hmap['descrizione'] ?? null; $idxAccrediti = $hmap['accrediti'] ?? null; $idxAddebiti = $hmap['addebiti'] ?? null; $idxDescrEstesa = $hmap['descrizioneestesa'] ?? null; if ($idxData === null || $idxDescr === null || ($idxAccrediti === null && $idxAddebiti === null)) { throw new \RuntimeException('Formato CSV non riconosciuto: colonne richieste mancanti'); } $rows = []; $skipped = 0; for ($i = $headerIndex + 1; $i < count($lines); $i++) { $line = (string) $lines[$i]; if (trim($line) === '') { continue; } $cols = str_getcsv($line, $delimiter); if (count($cols) < count($header)) { $cols = array_pad($cols, count($header), null); } $rawData = $cols[$idxData] ?? null; $data = $this->parseDate($rawData); if (! $data) { $skipped++; continue; } $valuta = null; if ($idxValuta !== null) { $valuta = $this->parseDate($cols[$idxValuta] ?? null); } $descrizione = trim((string) ($cols[$idxDescr] ?? '')); $descrEstesa = $idxDescrEstesa !== null ? trim((string) ($cols[$idxDescrEstesa] ?? '')) : ''; if ($descrizione === '' && $descrEstesa !== '') { $descrizione = $descrEstesa; } if ($descrizione === '') { $skipped++; continue; } $accrediti = $idxAccrediti !== null ? $this->parseMoney($cols[$idxAccrediti] ?? null) : null; $addebiti = $idxAddebiti !== null ? $this->parseMoney($cols[$idxAddebiti] ?? null) : null; $importo = null; if ($accrediti !== null && abs($accrediti) > 0.0) { $importo = abs($accrediti); } elseif ($addebiti !== null && abs($addebiti) > 0.0) { $importo = -abs($addebiti); } if ($importo === null) { $skipped++; continue; } $rows[] = [ 'data' => $data, 'valuta' => $valuta, 'descrizione' => $descrizione, 'importo' => (float) $importo, 'causale' => null, 'raw_line' => 'csv:' . ($i + 1), ]; } return [ 'rows' => $rows, 'meta' => [ 'header_row' => (int) ($headerIndex + 1), 'delimiter' => $delimiter, 'parsed_rows' => (int) count($rows), 'skipped_rows' => (int) $skipped, ], ]; } private function parseDate(mixed $value): ?Carbon { $s = trim((string) ($value ?? '')); if ($s === '') { return null; } foreach (['d/m/y', 'd/m/Y', 'd.m.Y', 'd.m.y', 'Y-m-d'] as $fmt) { try { return Carbon::createFromFormat($fmt, $s)->startOfDay(); } catch (\Throwable) { // keep trying } } return null; } private function parseMoney(mixed $value): ?float { if ($value === null) { return null; } $s = trim((string) $value); if ($s === '') { return null; } $s = str_replace(["\u{00A0}", "\t", ' '], '', $s); $s = str_ireplace(['EUR', 'EURO', '€'], '', $s); $s = preg_replace('/[^0-9,\.\-\+]/', '', $s) ?? $s; // formato italiano: 1.059,26 $s = str_replace('.', '', $s); $s = str_replace(',', '.', $s); if (! is_numeric($s)) { return null; } return (float) $s; } private function normHeader(string $value): string { $value = trim(mb_strtolower($value)); $value = str_replace([" ", "\t", "\u{00A0}"], '', $value); $value = str_replace(['à', 'á', 'â', 'ä'], 'a', $value); $value = str_replace(['è', 'é', 'ê', 'ë'], 'e', $value); $value = str_replace(['ì', 'í', 'î', 'ï'], 'i', $value); $value = str_replace(['ò', 'ó', 'ô', 'ö'], 'o', $value); $value = str_replace(['ù', 'ú', 'û', 'ü'], 'u', $value); $value = preg_replace('/[^a-z0-9]/', '', $value) ?? $value; return $value; } }