netgescon-day0/app/Console/Commands/ImportAffittiMdb.php

321 lines
13 KiB
PHP
Executable File

<?php
namespace App\Console\Commands;
use App\Models\AffittoCanoneDovuto;
use App\Models\AffittoCanonePagato;
use App\Models\AffittoImmobile;
use App\Models\Stabile;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Symfony\Component\Process\Process;
class ImportAffittiMdb extends Command
{
protected $signature = 'affitti:import-mdb
{--mdb= : Percorso a parti_comuni.mdb}
{--stabile= : Codice stabile (cod_stabile) per filtrare}
{--refresh-canoni : Cancella e reimporta canoni dovuti/pagati per gli affitti coinvolti}
{--truncate : Svuota tabelle affitti prima di importare}
';
protected $description = 'Importa Affitti, fitti_dovuti e Fitti_pagamenti da parti_comuni.mdb';
public function handle(): int
{
$mdb = $this->option('mdb') ?: '/mnt/gescon-archives/gescon/parti_comuni.mdb';
$filterStabile = $this->option('stabile');
if (! $mdb || ! is_file($mdb)) {
$this->error('Specifica --mdb con path valido al file parti_comuni.mdb');
return 1;
}
if ($this->option('truncate')) {
DB::statement('SET FOREIGN_KEY_CHECKS=0');
AffittoCanonePagato::truncate();
AffittoCanoneDovuto::truncate();
AffittoImmobile::truncate();
DB::statement('SET FOREIGN_KEY_CHECKS=1');
}
$this->info('Import Affitti…');
$affittiRows = $this->mdbExportRows($mdb, 'Affitti');
$affittiMap = [];
DB::beginTransaction();
try {
foreach ($affittiRows as $row) {
$codStabile = $this->norm($row['Cod_stabile'] ?? null);
if ($filterStabile && $codStabile !== $filterStabile) {
continue;
}
$codApp = $this->norm($row['Cod_appartamento'] ?? null);
if (! $codApp) {
continue;
}
$stabileId = $this->resolveStabileId($codStabile);
$record = AffittoImmobile::updateOrCreate(
[
'codice_stabile_legacy' => $codStabile,
'codice_appartamento' => $codApp,
],
[
'stabile_id' => $stabileId,
'proprietario_nome' => $this->norm($row['Proprietario_nome'] ?? null),
'proprietario_intesta' => $this->norm($row['Proprietario_intesta'] ?? null),
'descrizione_immobile' => $this->norm($row['Desriz_immobile'] ?? null),
'indirizzo_immobile' => $this->norm($row['Indirizzo_immob'] ?? null),
'cap' => $this->norm($row['Cap'] ?? null),
'citta' => $this->norm($row['Citta'] ?? null),
'provincia' => $this->norm($row['Pr'] ?? null),
'nome_inquilino' => $this->norm($row['Nome_inquilino'] ?? null),
'rendita_catastale' => $this->toFloat($row['Rendita_catastale'] ?? null),
'importo_fitto' => $this->toFloat($row['Importo_fitto'] ?? null),
'particella' => $this->norm($row['Particella'] ?? null),
'destinazione_uso' => $this->norm($row['Destinazione_d_uso'] ?? null),
'inizio_contratto' => $this->toDate($row['Inizio_contratto'] ?? null),
'ultimo_rinnovo' => $this->toDate($row['Ultimo_Rinnovo'] ?? null),
'prossima_scadenza' => $this->toDate($row['Prossima_scadenza'] ?? null),
'prossima_registrazione' => $this->toDate($row['Prossima_registrazione'] ?? null),
'note' => $this->norm($row['Note'] ?? null),
'def_ammin' => $this->norm($row['Def_ammin'] ?? null),
'descr_1_voce' => $this->norm($row['Descr_1_voce'] ?? null),
'tipo_riga' => $this->norm($row['tipo_riga'] ?? null),
'inte_cc' => $this->norm($row['Inte_cc'] ?? null),
'iban' => $this->norm($row['IBAN'] ?? null),
'selez' => $this->norm($row['Selez'] ?? null),
'num_ccp' => $this->norm($row['Num_CCP'] ?? null),
'autorizz_674' => $this->norm($row['Autorizz_674'] ?? null),
]
);
$affittiMap[$codApp] = $record->id;
}
DB::commit();
} catch (\Throwable $e) {
DB::rollBack();
$this->error('Errore import affitti: ' . $e->getMessage());
return 1;
}
if ($this->option('refresh-canoni') && ! empty($affittiMap)) {
$ids = array_values($affittiMap);
AffittoCanonePagato::whereIn('affitto_id', $ids)->delete();
AffittoCanoneDovuto::whereIn('affitto_id', $ids)->delete();
$this->line('Canoni esistenti rimossi per gli affitti importati.');
}
$this->info('Import fitti_dovuti…');
$dovutiRows = $this->mdbExportRows($mdb, 'fitti_dovuti');
$countDovuti = 0;
foreach ($dovutiRows as $row) {
$codApp = $this->norm($row['cod_appartamento'] ?? null);
if (! $codApp || ! isset($affittiMap[$codApp])) {
continue;
}
$affittoId = $affittiMap[$codApp];
$anno = $this->toInt($row['Anno'] ?? null);
$mese = $this->toInt($row['mese'] ?? null);
$payload = [
'mese_descrizione' => $this->norm($row['Mese_descrizione'] ?? null),
'fitto' => $this->toFloat($row['Fitto'] ?? null),
'istat_percentuale' => $this->toFloat($row['Istat_percentuale'] ?? null),
'istat_importo' => $this->toFloat($row['Istat_importo'] ?? null),
'descrizione_1' => $this->norm($row['Descrizione_1'] ?? null),
'descrizione_2' => $this->norm($row['Descrizione_2'] ?? null),
'descrizione_3' => $this->norm($row['Descrizione_3'] ?? null),
'descrizione_4' => $this->norm($row['Descrizione_4'] ?? null),
'importo_1' => $this->toFloat($row['Importo_1'] ?? null),
'importo_2' => $this->toFloat($row['Importo_2'] ?? null),
'importo_3' => $this->toFloat($row['Importo_3'] ?? null),
'importo_4' => $this->toFloat($row['Importo_4'] ?? null),
'bollo' => $this->toFloat($row['Bollo'] ?? null),
'totale' => $this->toFloat($row['Totale'] ?? null),
'n_ricevuta' => $this->norm($row['N_ricevuta'] ?? null),
];
if ($anno && $mese) {
AffittoCanoneDovuto::updateOrCreate(
[
'affitto_id' => $affittoId,
'anno' => $anno,
'mese' => $mese,
],
$payload
);
}
$countDovuti++;
}
$this->info('Import Fitti_pagamenti…');
$pagatiRows = $this->mdbExportRows($mdb, 'Fitti_pagamenti');
$countPagati = 0;
foreach ($pagatiRows as $row) {
$codApp = $this->norm($row['cod_appartamento'] ?? null);
if (! $codApp || ! isset($affittiMap[$codApp])) {
continue;
}
$affittoId = $affittiMap[$codApp];
$anno = $this->toInt($row['Anno'] ?? null);
$mese = $this->toInt($row['mese'] ?? null);
$dataPagamento = $this->toDate($row['Data_pagamento'] ?? null);
$totale = $this->toFloat($row['Totale'] ?? null);
$payload = [
'mese_descrizione' => $this->norm($row['Mese_descrizione'] ?? null),
'data_pagamento' => $dataPagamento,
'descrizione' => $this->norm($row['Descrizione'] ?? null),
'fitto' => $this->toFloat($row['Fitto'] ?? null),
'istat_percentuale' => $this->toFloat($row['Istat_percentuale'] ?? null),
'istat_importo' => $this->toFloat($row['Istat_importo'] ?? null),
'descrizione_1' => $this->norm($row['Descrizione_1'] ?? null),
'descrizione_2' => $this->norm($row['Descrizione_2'] ?? null),
'descrizione_3' => $this->norm($row['Descrizione_3'] ?? null),
'descrizione_4' => $this->norm($row['Descrizione_4'] ?? null),
'importo_1' => $this->toFloat($row['Importo_1'] ?? null),
'importo_2' => $this->toFloat($row['Importo_2'] ?? null),
'importo_3' => $this->toFloat($row['Importo_3'] ?? null),
'importo_4' => $this->toFloat($row['Importo_4'] ?? null),
'bollo' => $this->toFloat($row['Bollo'] ?? null),
'totale' => $totale,
];
if ($anno && $mese) {
AffittoCanonePagato::updateOrCreate(
[
'affitto_id' => $affittoId,
'anno' => $anno,
'mese' => $mese,
'data_pagamento' => $dataPagamento,
'totale' => $totale,
],
$payload
);
}
$countPagati++;
}
$this->info("OK: affitti=" . count($affittiMap) . ", dovuti={$countDovuti}, pagati={$countPagati}");
return 0;
}
private function resolveStabileId(?string $cod): ?int
{
if (! $cod) {
return null;
}
$q = Stabile::query();
if ($q->where('codice_stabile', $cod)->exists()) {
return (int) $q->where('codice_stabile', $cod)->value('id');
}
if ($q->where('cod_stabile', $cod)->exists()) {
return (int) $q->where('cod_stabile', $cod)->value('id');
}
$alt = ltrim($cod, '0');
if ($alt !== '' && $q->where('codice_stabile', $alt)->exists()) {
return (int) $q->where('codice_stabile', $alt)->value('id');
}
if ($alt !== '' && $q->where('cod_stabile', $alt)->exists()) {
return (int) $q->where('cod_stabile', $alt)->value('id');
}
$pad4 = str_pad($alt !== '' ? $alt : $cod, 4, '0', STR_PAD_LEFT);
if ($q->where('codice_stabile', $pad4)->exists()) {
return (int) $q->where('codice_stabile', $pad4)->value('id');
}
return null;
}
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 (! $header) {
return [];
}
$header = array_map(fn($h) => trim((string) $h), $header);
$rows = [];
while (($row = fgetcsv($fh)) !== false) {
if ($row === [null]) {
continue;
}
$assoc = [];
foreach ($header as $i => $col) {
$assoc[$col] = $row[$i] ?? null;
}
$rows[] = $assoc;
}
fclose($fh);
return $rows;
}
private function norm($v): ?string
{
$s = trim((string) ($v ?? ''));
return $s === '' ? null : $s;
}
private function toFloat($v): ?float
{
if ($v === null || $v === '') {
return null;
}
$raw = trim((string) $v);
if (stripos($raw, 'e') !== false) {
return is_numeric($raw) ? (float) $raw : null;
}
if (str_contains($raw, ',')) {
$s = str_replace('.', '', $raw);
$s = str_replace(',', '.', $s);
return is_numeric($s) ? (float) $s : null;
}
return is_numeric($raw) ? (float) $raw : null;
}
private function toInt($v): ?int
{
if ($v === null || $v === '') {
return null;
}
return is_numeric($v) ? (int) $v : null;
}
private function toDate($v): ?string
{
$s = trim((string) ($v ?? ''));
if ($s === '') {
return null;
}
$dt = \DateTime::createFromFormat('Y-m-d', $s)
?: \DateTime::createFromFormat('d/m/Y', $s)
?: \DateTime::createFromFormat('m/d/Y', $s);
if ($dt) {
return $dt->format('Y-m-d');
}
$ts = @strtotime($s);
return $ts ? date('Y-m-d', $ts) : null;
}
}