/dev/null', escapeshellarg($binTables), escapeshellarg($mdbPath))) ?: ''; $table = null; if ($tables) { $names = array_filter(preg_split('/\r?\n/', trim($tables))); foreach ($tableCandidates as $cand) { foreach ($names as $n) { if (strcasecmp($n, $cand) === 0) { $table = $n; break 2; } } } if (! $table) { foreach ($tableCandidates as $cand) { foreach ($names as $n) { if (stripos($n, $cand) !== false) { $table = $n; break 2; } } } } } if (! $table) { return []; } $csv = @shell_exec(sprintf('%s -d "," %s %s 2>/dev/null', escapeshellarg($binExport), escapeshellarg($mdbPath), escapeshellarg($table))) ?: ''; if ($csv === '') { return []; } $lines = preg_split('/\r?\n/', trim($csv)); if (count($lines) < 2) { return []; } $headers = str_getcsv(array_shift($lines)); $headers = array_map(fn($h) => strtolower(trim((string)$h)), $headers); $results = []; foreach ($lines as $line) { if (trim($line) === '') { continue; } $row = str_getcsv($line); if (count($row) !== count($headers)) { $row = array_pad($row, count($headers), null); } $results[] = array_combine($headers, $row); } return $results; } /** * Importa le letture acqua dal file master generale_stabile.mdb. */ public function importLettureAcqua(string $stabileCode, string $rootPath): int { $generaleMdb = rtrim($rootPath, '/') . '/' . $stabileCode . '/generale_stabile.mdb'; if (! is_file($generaleMdb)) { return 0; } // 1. Build map from acqua_gen: Rif_ute => Rif_anno $acquaGen = $this->mdbExportToRows($generaleMdb, ['acqua_gen', 'Acqua_gen', 'ACQUA_GEN']); $acquaGenMap = []; foreach ($acquaGen as $gen) { $rifUte = $gen['rif_ute'] ?? null; $rifAnno = $gen['rif_anno'] ?? null; if ($rifUte !== null) { $acquaGenMap[$rifUte] = $rifAnno; } } // 2. Load acqua_dett rows (including versioned variants like acqua_dett1) $rows1 = $this->mdbExportToRows($generaleMdb, ['acqua_dett', 'Acqua_dett', 'ACQUA_DETT']); $rows2 = $this->mdbExportToRows($generaleMdb, ['acqua_dett1', 'Acqua_dett1', 'ACQUA_DETT1']); $acquaDett = array_merge($rows1, $rows2); if (empty($acquaDett)) { return 0; } // 3. Resolve Stabile ID $stabile = DB::table('stabili') ->where('codice_stabile', $stabileCode) ->first(); if (! $stabile) { return 0; } $count = 0; foreach ($acquaDett as $row) { $rifUte = $row['rif_ute'] ?? null; $idCond = $row['id_cond'] ?? null; $condInq = strtoupper(trim((string) ($row['cond_inquil'] ?? 'C'))); $tCodCond = trim((string) ($row['t_cod_cond'] ?? '')); if ($idCond === null || $rifUte === null) { continue; } // Resolve year from general map $year = $acquaGenMap[$rifUte] ?? '2025'; // Find Unita ID by legacy_cond_id $unita = DB::table('unita_immobiliari') ->where('stabile_id', $stabile->id) ->where('legacy_cond_id', $idCond) ->first(); $unitaId = $unita?->id ?? null; if (! $unitaId) { // Try fallback to staging condomin $condomin = DB::connection('gescon_import') ->table('condomin') ->where('cod_stabile', $stabileCode) ->where('id_cond', $idCond) ->first(); if ($condomin) { $scala = $condomin->scala ? trim((string) $condomin->scala) : 'A'; $interno = $condomin->interno ? trim((string) $condomin->interno) : ''; if ($interno !== '') { $unita = DB::table('unita_immobiliari') ->where('stabile_id', $stabile->id) ->where('scala', $scala) ->where('interno', $interno) ->first(); $unitaId = $unita?->id ?? null; } } } if (! $unitaId) { continue; } // Validate: check t_cod_cond match in unita_anagrafica_periodo for that period/year $dataLettura = $year . '-12-31'; $periodi = DB::table('unita_anagrafica_periodo') ->where('unita_immobiliare_id', $unitaId) ->where('data_inizio', '<=', $dataLettura) ->where(function ($q) use ($dataLettura) { $q->whereNull('data_fine')->orWhere('data_fine', '>=', $dataLettura); }) ->get(); $valid = false; foreach ($periodi as $p) { $meta = json_decode($p->legacy_metadata, true); $pCodCond = trim((string) ($meta['cod_cond'] ?? '')); if ($pCodCond !== '' && strcasecmp($pCodCond, $tCodCond) === 0) { $valid = true; break; } } if (! $valid) { // If not valid in pivot timeline, skip reading as requested continue; } // Resolve readings $readings = [ [ 'precedente' => isset($row['lett_vecchia_1']) ? (float) $row['lett_vecchia_1'] : 0.0, 'attuale' => isset($row['lett_nuova_1']) ? (float) $row['lett_nuova_1'] : 0.0, 'num' => 1 ], [ 'precedente' => isset($row['lett_vecchia_2']) ? (float) $row['lett_vecchia_2'] : 0.0, 'attuale' => isset($row['lett_nuova_2']) ? (float) $row['lett_nuova_2'] : 0.0, 'num' => 2 ], [ 'precedente' => isset($row['lett_vecchia_3']) ? (float) $row['lett_vecchia_3'] : 0.0, 'attuale' => isset($row['lett_nuova_3']) ? (float) $row['lett_nuova_3'] : 0.0, 'num' => 3 ] ]; foreach ($readings as $reading) { if ($reading['attuale'] == 0.0 && $reading['precedente'] == 0.0) { continue; } // Find or create Contatore for this unit $contatore = Contatore::firstOrCreate( [ 'stabile_id' => $stabile->id, 'unita_immobiliare_id' => $unitaId, 'tipo_contatore' => 'acqua_fredda', 'numero_contatore' => 'ACQ-' . $unitaId . '-' . $reading['num'], ], [ 'attivo' => true, 'note' => 'Creato automaticamente da importazione acqua_dett', ] ); // Insert reading LetturaContatore::updateOrCreate( [ 'contatore_id' => $contatore->id, 'data_lettura' => $dataLettura, ], [ 'lettura_precedente' => $reading['precedente'], 'lettura_attuale' => $reading['attuale'], 'tipo_lettura' => 'manuale', 'validata' => true, 'note' => 'Importata da acqua_dett. Ruolo legacy: ' . $condInq . ', cod_cond: ' . $tCodCond, ] ); $count++; } } return $count; } }