netgescon-day0/app/Services/GesconCoreMigrationService.php

252 lines
8.9 KiB
PHP

<?php
namespace App\Services;
use App\Models\Contatore;
use App\Models\LetturaContatore;
use App\Models\UnitaImmobiliare;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Carbon\Carbon;
class GesconCoreMigrationService
{
/**
* Esporta una tabella MDB in array di righe associative.
*/
private function mdbExportToRows(string $mdbPath, array $tableCandidates): array
{
$binTables = trim((string) @shell_exec('command -v mdb-tables')) ?: '/usr/bin/mdb-tables';
$binExport = trim((string) @shell_exec('command -v mdb-export')) ?: '/usr/bin/mdb-export';
if (! is_file($mdbPath) || ! is_readable($mdbPath)) {
return [];
}
$tables = @shell_exec(sprintf('%s -1 %s 2>/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;
}
}