option('mdb') ?: '/mnt/gescon-archives/gescon/parti_comuni.mdb'; $filterStabile = trim((string) $this->option('stabile')); if (! is_file($mdb)) { $this->error('Specifica --mdb con path valido al file parti_comuni.mdb'); return self::FAILURE; } if ((bool) $this->option('truncate')) { Scadenza::query()->truncate(); } $rows = $this->mdbExportRows($mdb, 'Scadenze'); if ($rows === []) { $this->warn('Nessuna riga letta dalla tabella Scadenze.'); return self::SUCCESS; } $imported = 0; foreach ($rows as $row) { $legacyId = $this->toInt($row['id_scadenza'] ?? null); if ($legacyId <= 0) { continue; } $codStabile = $this->normalizeString($row['cod_stabile'] ?? null); if ($filterStabile !== '' && $codStabile !== $filterStabile) { continue; } $natura = strtoupper($this->normalizeString($row['Natura'] ?? null) ?? ''); Scadenza::query()->updateOrCreate( ['legacy_id' => $legacyId], [ 'stabile_id' => $this->resolveStabileId($codStabile), 'codice_stabile_legacy' => $codStabile, 'descrizione_sintetica' => $this->normalizeString($row['Descriz_sintetica'] ?? null), 'data_scadenza' => $this->toDate($row['Dt_scadenza'] ?? null), 'ora_scadenza' => $this->toTime($row['ora_scadenza'] ?? null), 'natura' => $natura !== '' ? $natura : null, 'natura_label' => $this->resolveNaturaLabel($natura), 'gestione_tipo' => $this->resolveGestioneTipo($row['Gest_ors'] ?? null), 'num_assemblea' => $this->toInt($row['Num_assemblea'] ?? null) ?: null, 'numero_straordinaria' => $this->toInt($row['N_stra'] ?? null) ?: null, 'anno' => $this->normalizeString($row['Anno'] ?? null), 'rata' => $this->toInt($row['Rata'] ?? null) ?: null, 'fatto' => $this->normalizeString($row['Fatto'] ?? null), 'tipo_riga' => $this->normalizeString($row['tipo_riga'] ?? null), 'rif_f24' => $this->toInt($row['Rif_f24'] ?? null) ?: null, 'importo_f24' => $this->toDecimal($row['Importo_f24'] ?? null), 'richiede_pop_up' => $this->toBoolean($row['Richiede_pop_up'] ?? null), 'giorni_anticipo' => $this->toInt($row['gg_anticipo'] ?? null) ?: null, 'popup_dal' => $this->toDate($row['Pop_Up_dal'] ?? null), 'legacy_payload' => [ 'legacy_id' => $legacyId, 'cod_stabile' => $codStabile, 'descriz_sintetica' => $this->normalizeString($row['Descriz_sintetica'] ?? null), 'gest_ors' => $this->normalizeString($row['Gest_ors'] ?? null), 'natura' => $natura, 'tipo_riga' => $this->normalizeString($row['tipo_riga'] ?? null), 'num_assemblea' => $this->toInt($row['Num_assemblea'] ?? null) ?: null, 'numero_straordinaria'=> $this->toInt($row['N_stra'] ?? null) ?: null, 'anno' => $this->normalizeString($row['Anno'] ?? null), 'rata' => $this->toInt($row['Rata'] ?? null) ?: null, ], ] ); $imported++; } $this->info('Scadenze importate/aggiornate: ' . $imported); return self::SUCCESS; } private function mdbExportRows(string $mdb, string $table): array { $bin = trim((string) shell_exec('command -v mdb-export')) ?: '/usr/bin/mdb-export'; $process = new Process([$bin, $mdb, $table]); $process->setTimeout(120); $process->run(); if (! $process->isSuccessful()) { $this->warn("mdb-export fallito per {$table}: " . $process->getErrorOutput()); return []; } $csv = $process->getOutput(); if ($csv === '') { return []; } $fh = fopen('php://temp', 'r+'); fwrite($fh, $csv); rewind($fh); $header = fgetcsv($fh); if (! is_array($header)) { return []; } $header = array_map(static fn($value) => trim((string) $value), $header); $rows = []; while (($row = fgetcsv($fh)) !== false) { if ($row === [null]) { continue; } $assoc = []; foreach ($header as $index => $column) { $assoc[$column] = $row[$index] ?? null; } $rows[] = $assoc; } fclose($fh); return $rows; } private function resolveStabileId(?string $cod): ?int { if ($cod === null || $cod === '') { return null; } $query = Stabile::query(); $variants = array_values(array_unique(array_filter([ $cod, ltrim($cod, '0'), str_pad(ltrim($cod, '0') !== '' ? ltrim($cod, '0') : $cod, 4, '0', STR_PAD_LEFT), ], static fn($value) => $value !== ''))); foreach ($variants as $variant) { $id = $query->clone()->where('codice_stabile', $variant)->value('id'); if ($id) { return (int) $id; } $id = $query->clone()->where('cod_stabile', $variant)->value('id'); if ($id) { return (int) $id; } } return null; } private function normalizeString(mixed $value): ?string { $normalized = trim((string) $value); return $normalized !== '' ? $normalized : null; } private function toInt(mixed $value): int { $normalized = $this->normalizeString($value); return $normalized !== null ? (int) preg_replace('/[^0-9-]+/', '', $normalized) : 0; } private function toDecimal(mixed $value): ?float { $normalized = $this->normalizeString($value); if ($normalized === null) { return null; } if (str_contains($normalized, ',') && str_contains($normalized, '.')) { $normalized = str_replace('.', '', $normalized); $normalized = str_replace(',', '.', $normalized); } elseif (str_contains($normalized, ',')) { $normalized = str_replace(',', '.', $normalized); } return is_numeric($normalized) ? round((float) $normalized, 2) : null; } private function toBoolean(mixed $value): bool { $normalized = strtolower($this->normalizeString($value) ?? ''); return in_array($normalized, ['1', 'si', 'sì', 'true', 'yes', 'y'], true); } private function toDate(mixed $value): ?string { $normalized = $this->normalizeString($value); if ($normalized === null) { return null; } $formats = ['m/d/y H:i:s', 'm/d/Y H:i:s', 'm/d/y', 'm/d/Y']; foreach ($formats as $format) { $date = \DateTimeImmutable::createFromFormat($format, $normalized); if ($date instanceof \DateTimeImmutable) { return $date->format('Y-m-d'); } } return null; } private function toTime(mixed $value): ?string { $normalized = $this->normalizeString($value); if ($normalized === null) { return null; } $formats = ['H:i:s', 'H:i']; foreach ($formats as $format) { $date = \DateTimeImmutable::createFromFormat($format, $normalized); if ($date instanceof \DateTimeImmutable) { return $date->format('H:i:s'); } } return null; } private function resolveGestioneTipo(mixed $value): ?string { return match (strtoupper($this->normalizeString($value) ?? '')) { 'O' => 'ordinaria', 'S' => 'straordinaria', default => $this->normalizeString($value), }; } private function resolveNaturaLabel(?string $value): ?string { return match (strtoupper($value ?? '')) { 'F' => 'Versamento F24', 'R' => 'Rata / emissione rata', default => $this->normalizeString($value), }; } }