559 lines
22 KiB
PHP
559 lines
22 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\Amministratore;
|
||
use App\Models\FeCassettoServiceConfig;
|
||
use App\Models\Stabile;
|
||
use App\Models\UnitaImmobiliare;
|
||
use App\Models\StabileServizioLettura;
|
||
use Illuminate\Support\Facades\Http;
|
||
use Illuminate\Support\Facades\Storage;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Exception;
|
||
use ZipArchive;
|
||
|
||
class SisterCatastoService
|
||
{
|
||
public static function scomponeIndirizzoAde(string $indirizzoCompleto, string $pianoStringa = ''): array
|
||
{
|
||
$via = '';
|
||
$scala = '';
|
||
$interno = '';
|
||
$piano = '';
|
||
$pianoLivello = '';
|
||
$pianoFisico = '';
|
||
|
||
if (preg_match('/Piano\s+(.+)$/i', $indirizzoCompleto, $m)) {
|
||
$piano = trim($m[1]);
|
||
}
|
||
if (preg_match('/Interno\s+(\d+)/i', $indirizzoCompleto, $m)) {
|
||
$interno = trim($m[1]);
|
||
}
|
||
if (preg_match('/Scala\s+([A-Z])/i', $indirizzoCompleto, $m)) {
|
||
$scala = trim($m[1]);
|
||
}
|
||
$parti = preg_split('/(Scala|Interno|Piano)/i', $indirizzoCompleto);
|
||
if (isset($parti[0])) {
|
||
$via = trim(rtrim(trim($parti[0]), ','));
|
||
}
|
||
|
||
if (empty($piano) && !empty($pianoStringa)) {
|
||
$piano = trim(str_ireplace('Piano', '', $pianoStringa));
|
||
}
|
||
|
||
if (!empty($piano)) {
|
||
if (preg_match('/(\d+|S\d+|T|PT|Terra)\s*-\s*(\d+|S\d+|T|PT|Terra)/i', $piano, $m)) {
|
||
$val1 = strtoupper(trim($m[1]));
|
||
$val2 = strtoupper(trim($m[2]));
|
||
if (str_starts_with($val1, 'S')) {
|
||
$pianoLivello = $val1;
|
||
$pianoFisico = $val2;
|
||
} elseif (str_starts_with($val2, 'S')) {
|
||
$pianoLivello = $val2;
|
||
$pianoFisico = $val1;
|
||
} else {
|
||
$pianoLivello = $val1;
|
||
$pianoFisico = $val2;
|
||
}
|
||
} else {
|
||
$pUpper = strtoupper(trim($piano));
|
||
if (str_starts_with($pUpper, 'S')) {
|
||
$pianoLivello = $pUpper;
|
||
$pianoFisico = '';
|
||
} else {
|
||
$pianoLivello = '';
|
||
$pianoFisico = $pUpper;
|
||
}
|
||
}
|
||
}
|
||
|
||
return [
|
||
'catasto_via' => $via,
|
||
'catasto_scala' => $scala,
|
||
'catasto_interno' => $interno,
|
||
'catasto_piano' => $piano,
|
||
'catasto_piano_livello' => $pianoLivello,
|
||
'catasto_piano_fisico' => $pianoFisico
|
||
];
|
||
}
|
||
|
||
public static function formattaCategoriaCatastale(string $codice): string
|
||
{
|
||
$codiceClean = strtoupper(trim(str_replace([' ', '/', '-', '_'], '', $codice)));
|
||
|
||
if (preg_match('/^([A-F])(\d)$/', $codiceClean, $m)) {
|
||
$codiceClean = $m[1] . '0' . $m[2];
|
||
}
|
||
|
||
$mappa = [
|
||
'A01' => 'A/1 - Abitazioni di tipo signorile',
|
||
'A02' => 'A/2 - Abitazioni di tipo civile',
|
||
'A03' => 'A/3 - Abitazioni di tipo economico',
|
||
'A04' => 'A/4 - Abitazioni di tipo popolare',
|
||
'A05' => 'A/5 - Abitazioni di tipo ultrapopolare',
|
||
'A06' => 'A/6 - Abitazioni di tipo rurale',
|
||
'A07' => 'A/7 - Abitazioni in villini',
|
||
'A08' => 'A/8 - Abitazioni in ville',
|
||
'A09' => 'A/9 - Castelli, palazzi di eminenti pregi artistici o storici',
|
||
'A10' => 'A/10 - Uffici e studi privati',
|
||
'A11' => 'A/11 - Abitazioni ed alloggi tipici dei luoghi',
|
||
|
||
'B01' => 'B/1 - Collegi e convitti, educandati, ricoveri, orfanotrofi, ospizi, conventi, seminari, caserme',
|
||
'B02' => 'B/2 - Case di cura ed ospedali (senza fine di lucro)',
|
||
'B03' => 'B/3 - Prigioni e riformatori',
|
||
'B04' => 'B/4 - Uffici pubblici',
|
||
'B05' => 'B/5 - Scuole e laboratori scientifici',
|
||
'B06' => 'B/6 - Biblioteche, pinacoteche, musei, gallerie, accademie che non hanno sede in edifici della categoria A/9',
|
||
'B07' => 'B/7 - Cappelle ed oratori non destinati all’esercizio pubblico del culto',
|
||
'B08' => 'B/8 - Magazzini sotterranei per depositi di derrate',
|
||
|
||
'C01' => 'C/1 - Negozi e botteghe',
|
||
'C02' => 'C/2 - Magazzini e locali di deposito',
|
||
'C03' => 'C/3 - Laboratori per arti e mestieri',
|
||
'C04' => 'C/4 - Fabbricati e locali per esercizi sportivi (senza fine di lucro)',
|
||
'C05' => 'C/5 - Stabilimenti balneari e di acque curative (senza fine di lucro)',
|
||
'C06' => 'C/6 - Stalle, scuderie, rimesse, autorimesse (senza fine di lucro)',
|
||
'C07' => 'C/7 - Tettoie chiuse o aperte',
|
||
|
||
'D01' => 'D/1 - Opifici',
|
||
'D02' => 'D/2 - Alberghi e pensioni (con fine di lucro)',
|
||
'D03' => 'D/3 - Teatri, cinematografi, sale per concerti e spettacoli e simili (con fine di lucro)',
|
||
'D04' => 'D/4 - Case di cura ed ospedali (con fine di lucro)',
|
||
'D05' => 'D/5 - Istituto di credito, cambio e assicurazione (con fine di lucro)',
|
||
'D06' => 'D/6 - Fabbricati e locali per esercizi sportivi (con fine di lucro)',
|
||
'D07' => 'D/7 - Fabbricati costruiti o adattati per le speciali esigenze di un’attività industriale e non suscettibili di destinazione diversa senza radicali trasformazioni',
|
||
'D08' => 'D/8 - Fabbricati costruiti o adattati per le speciali esigenze di un’attività commerciale e non suscettibili di destinazione diversa senza radicali trasformazioni',
|
||
'D09' => 'D/9 - Edifici galleggianti o assicurati a punti fissi del suolo, ponti privati soggetti a pedaggio',
|
||
'D10' => 'D/10 - Fabbricati per funzioni produttive connesse alle attività agricole',
|
||
|
||
'E01' => 'E/1 - Stazioni per servizi di trasporto, terrestri, marittimi ed aerei',
|
||
'E02' => 'E/2 - Ponti comunali e provinciali soggetti a pedaggio',
|
||
'E03' => 'E/3 - Costruzioni e fabbricati per speciali esigenze pubbliche',
|
||
'E04' => 'E/4 - Recinti chiusi per speciali esigenze pubbliche',
|
||
'E05' => 'E/5 - Fabbricati costituenti fortificazioni e loro dipendenze',
|
||
'E06' => 'E/6 - Fari, semafori, torri per rendere pubblico l’uso dell’orologio comunale',
|
||
'E07' => 'E/7 - Fabbricati destinati all’esercizio pubblico dei culti',
|
||
'E08' => 'E/8 - Fabbricati e costruzioni nei cimiteri, esclusi i colombari, i sepolcri e le tombe di famiglia',
|
||
'E09' => 'E/9 - Fabbricati a destinazione particolare non compresi nelle categorie precedenti della categoria E',
|
||
|
||
'F01' => 'F/1 - Area urbana',
|
||
'F02' => 'F/2 - Unità collabenti',
|
||
'F03' => 'F/3 - Unità in corso di costruzione',
|
||
'F04' => 'F/4 - Unità in corso di definizione',
|
||
'F05' => 'F/5 - Lastrico solare',
|
||
'F06' => 'F/6 - Fabbricato in attesa di dichiarazione',
|
||
'F07' => 'F/7 - Infrastrutture di reti pubbliche di comunicazione',
|
||
];
|
||
|
||
if (isset($mappa[$codiceClean])) {
|
||
return $mappa[$codiceClean];
|
||
}
|
||
|
||
if (preg_match('/^([A-F])0?(\d+)$/', $codiceClean, $m)) {
|
||
return $m[1] . '/' . $m[2];
|
||
}
|
||
|
||
return $codice;
|
||
}
|
||
|
||
public static function estraiCodiceCompatto(string $categoria): string
|
||
{
|
||
if (str_contains($categoria, '-')) {
|
||
$parti = explode('-', $categoria);
|
||
return trim($parti[0]);
|
||
}
|
||
$codiceClean = strtoupper(trim(str_replace([' ', '/', '-', '_'], '', $categoria)));
|
||
if (preg_match('/^([A-F])0?(\d+)$/', $codiceClean, $m)) {
|
||
return $m[1] . '/' . $m[2];
|
||
}
|
||
return $categoria;
|
||
}
|
||
|
||
public static function pulisciRendita(mixed $val): float
|
||
{
|
||
if ($val === null || $val === '') {
|
||
return 0.0;
|
||
}
|
||
$valStr = (string)$val;
|
||
$clean = preg_replace('/[^\d\,\.]/', '', $valStr);
|
||
if (str_contains($clean, ',') && !str_contains($clean, '.')) {
|
||
$clean = str_replace(',', '.', $clean);
|
||
} elseif (str_contains($clean, ',') && str_contains($clean, '.')) {
|
||
if (strrpos($clean, ',') > strrpos($clean, '.')) {
|
||
$clean = str_replace('.', '', $clean);
|
||
$clean = str_replace(',', '.', $clean);
|
||
} else {
|
||
$clean = str_replace(',', '', $clean);
|
||
}
|
||
}
|
||
return round((float)$clean, 2);
|
||
}
|
||
/**
|
||
* Resolve credentials: extracts only passwordScript and base_url from FeCassettoServiceConfig,
|
||
* with an optional fallback to the active administrator (ID=4).
|
||
*/
|
||
protected function resolveCredentials(?int $stabileId): array
|
||
{
|
||
$cfg = FeCassettoServiceConfig::query()->first();
|
||
|
||
$passwordScript = $cfg ? trim((string)$cfg->password_script) : '';
|
||
$baseUrl = $cfg ? trim((string)$cfg->base_url) : 'https://thenetworksolution.it/FattureCorrispettivi/ScaricaFatture/CassettoFiscale.php';
|
||
$appUrl = config('app.url') ?: 'http://localhost';
|
||
|
||
// Fallback opzionale sull'amministratore con ID = 4 se vuota
|
||
if (empty($passwordScript)) {
|
||
$admin = Amministratore::find(4) ?: Amministratore::first();
|
||
if ($admin) {
|
||
$passwordScript = trim((string)$admin->fe_cassetto_password_script);
|
||
}
|
||
}
|
||
|
||
if (empty($passwordScript)) {
|
||
$passwordScript = 'demo';
|
||
}
|
||
|
||
return [
|
||
'passwordScript' => $passwordScript,
|
||
'base_url' => $baseUrl,
|
||
'app_url' => $appUrl,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Get multi-tenant storage directory for the stable.
|
||
*/
|
||
public function getStoragePath(int $stabileId): string
|
||
{
|
||
$stabile = Stabile::find($stabileId);
|
||
$amministratore = $stabile?->amministratore;
|
||
|
||
$adminCode = $amministratore ? trim((string)$amministratore->codice_amministratore) : 'ADM';
|
||
$adminFolder = "AMMINISTRATORE_" . str_replace(' ', '_', $adminCode);
|
||
|
||
$stabileCode = $stabile ? trim((string)$stabile->codice_stabile) : 'STB';
|
||
$stabileName = $stabile ? trim((string)$stabile->denominazione) : 'STABILE';
|
||
$stabileFolder = "STABILE_" . str_replace(' ', '_', "{$stabileCode}_{$stabileName}");
|
||
|
||
$adminFolder = preg_replace('/[^a-zA-Z0-9_]/', '', $adminFolder);
|
||
$stabileFolder = preg_replace('/[^a-zA-Z0-9_]/', '', $stabileFolder);
|
||
|
||
$adminFolder = preg_replace('/_+/', '_', $adminFolder);
|
||
$stabileFolder = preg_replace('/_+/', '_', $stabileFolder);
|
||
|
||
return "{$adminFolder}/{$stabileFolder}/catasto";
|
||
}
|
||
|
||
/**
|
||
* Metodo A: Ricerca Immobili per Soggetto (PF / PG)
|
||
*/
|
||
public function ricercaImmobiliSoggetto(int $stabileId, string $cfOrPg, string $uffProvinciale, string $tipoCatasto = 'E'): array
|
||
{
|
||
$creds = $this->resolveCredentials($stabileId);
|
||
|
||
$params = [
|
||
'passwordScript' => $creds['passwordScript'],
|
||
'app_url' => $creds['app_url'],
|
||
'tipoCatasto' => $tipoCatasto,
|
||
'uffprovinciale' => $uffProvinciale,
|
||
'easy' => 1,
|
||
];
|
||
|
||
if (strlen($cfOrPg) === 16) {
|
||
$params['cf'] = strtoupper($cfOrPg);
|
||
} else {
|
||
$params['pg'] = $cfOrPg;
|
||
}
|
||
|
||
$url = str_replace('ScaricaFatture/CassettoFiscale.php', 'sister/mashImmobili.php', $creds['base_url']);
|
||
$url = str_replace('FattureCorrispettivi/sister/', 'sister/', $url);
|
||
|
||
$response = Http::asForm()->post($url, $params);
|
||
|
||
if ($response->failed()) {
|
||
throw new Exception("Errore chiamata Sister API: " . $response->body());
|
||
}
|
||
|
||
return $response->json() ?? [];
|
||
}
|
||
|
||
/**
|
||
* Metodo B: Ricerca Intestati di Immobile
|
||
*/
|
||
public function ricercaIntestatiImmobile(
|
||
int $stabileId,
|
||
string $tipoCatasto,
|
||
string $uffProvinciale,
|
||
string $denomComune,
|
||
?string $foglio,
|
||
?string $particella,
|
||
?string $subalterno = null,
|
||
?string $sezione = null,
|
||
?string $sezUrb = null
|
||
): array {
|
||
$creds = $this->resolveCredentials($stabileId);
|
||
|
||
$params = [
|
||
'passwordScript' => $creds['passwordScript'],
|
||
'app_url' => $creds['app_url'],
|
||
'tipoCatasto' => $tipoCatasto,
|
||
'uffprovinciale' => $uffProvinciale,
|
||
'denomComune' => $denomComune,
|
||
'foglio' => $foglio ?? '',
|
||
'particella' => $particella ?? '',
|
||
'easy' => 1,
|
||
];
|
||
|
||
if ($subalterno !== null && $subalterno !== '') {
|
||
$params['subalterno'] = $subalterno;
|
||
}
|
||
if ($sezione !== null && $sezione !== '') {
|
||
$params['sezione'] = $sezione;
|
||
}
|
||
if ($sezUrb !== null && $sezUrb !== '') {
|
||
$params['sezUrb'] = $sezUrb;
|
||
}
|
||
|
||
$url = str_replace('ScaricaFatture/CassettoFiscale.php', 'sister/mashIntestati.php', $creds['base_url']);
|
||
$url = str_replace('FattureCorrispettivi/sister/', 'sister/', $url);
|
||
|
||
$response = Http::asForm()->post($url, $params);
|
||
|
||
if ($response->failed()) {
|
||
throw new Exception("Errore chiamata Sister API: " . $response->body());
|
||
}
|
||
|
||
return $response->json() ?? [];
|
||
}
|
||
|
||
/**
|
||
* Metodo C: Acquisto Visura Ufficiale (PDF / XML Firmato)
|
||
* Gestisce la cache locale e il polling per i documenti in evasione.
|
||
*/
|
||
public function acquistaEPrelevaVisura(
|
||
int $stabileId,
|
||
string $tipoCatasto,
|
||
string $uffProvinciale,
|
||
string $denomComune,
|
||
?string $foglio,
|
||
?string $particella,
|
||
?string $subalterno = null,
|
||
?string $sezione = null,
|
||
?string $sezUrb = null,
|
||
string $formato = 'PDF',
|
||
string $tipoVisura = 'completa',
|
||
int $cacheHours = 24
|
||
): array {
|
||
$creds = $this->resolveCredentials($stabileId);
|
||
$dirPath = $this->getStoragePath($stabileId);
|
||
|
||
// Genera un nome file locale univoco per la visura
|
||
$subLabel = ($subalterno !== null && $subalterno !== '') ? "_sub{$subalterno}" : "";
|
||
$foglioClean = $foglio ?? 'empty';
|
||
$particellaClean = $particella ?? 'empty';
|
||
$localFilename = "visura_{$tipoCatasto}_{$foglioClean}_{$particellaClean}{$subLabel}." . strtolower($formato);
|
||
$localRelativePath = "{$dirPath}/{$localFilename}";
|
||
|
||
// Verifica cache se le ore sono > 0 e il file esiste localmente
|
||
if ($cacheHours > 0 && Storage::disk('public')->exists($localRelativePath)) {
|
||
$lastModified = Storage::disk('public')->lastModified($localRelativePath);
|
||
if (time() - $lastModified < ($cacheHours * 3600)) {
|
||
return [
|
||
'status' => 'success',
|
||
'cached' => true,
|
||
'file_path' => $localRelativePath,
|
||
'url' => Storage::disk('public')->url($localRelativePath),
|
||
];
|
||
}
|
||
}
|
||
|
||
$params = [
|
||
'passwordScript' => $creds['passwordScript'],
|
||
'app_url' => $creds['app_url'],
|
||
'tipoCatasto' => $tipoCatasto,
|
||
'uffprovinciale' => $uffProvinciale,
|
||
'denomComune' => $denomComune,
|
||
'foglio' => $foglio ?? '',
|
||
'particella' => $particella ?? '',
|
||
'formato' => strtoupper($formato),
|
||
'tipoVisura' => $tipoVisura,
|
||
'cache' => $cacheHours,
|
||
];
|
||
|
||
if ($subalterno !== null && $subalterno !== '') {
|
||
$params['subalterno'] = $subalterno;
|
||
}
|
||
if ($sezione !== null && $sezione !== '') {
|
||
$params['sezione'] = $sezione;
|
||
}
|
||
if ($sezUrb !== null && $sezUrb !== '') {
|
||
$params['sezUrb'] = $sezUrb;
|
||
}
|
||
|
||
$url = str_replace('ScaricaFatture/CassettoFiscale.php', 'sister/mashVisuraI.php', $creds['base_url']);
|
||
$url = str_replace('FattureCorrispettivi/sister/', 'sister/', $url);
|
||
|
||
$response = Http::asForm()->post($url, $params);
|
||
|
||
if ($response->failed()) {
|
||
throw new Exception("Errore chiamata Sister API per visura: " . $response->body());
|
||
}
|
||
|
||
$result = $response->json();
|
||
|
||
// Se la richiesta è in evasione differita
|
||
if (isset($result['idRichiesta'])) {
|
||
return $this->pollRichiestaEvasione($stabileId, (int)$result['idRichiesta'], $localFilename);
|
||
}
|
||
|
||
if (isset($result['url'])) {
|
||
$fileUrl = $result['url'];
|
||
if (!str_starts_with($fileUrl, 'http')) {
|
||
$fileUrl = "https://thenetworksolution.it/sister/" . ltrim($fileUrl, '/');
|
||
}
|
||
|
||
return $this->scaricaEArchiviaFile($stabileId, $fileUrl, $localFilename);
|
||
}
|
||
|
||
throw new Exception("Risposta Sister non riconosciuta: " . $response->body());
|
||
}
|
||
|
||
/**
|
||
* Esegue il download del file (PDF/XML o ZIP) e lo memorizza sul Cloud dello stabile.
|
||
*/
|
||
protected function scaricaEArchiviaFile(int $stabileId, string $url, string $targetFilename): array
|
||
{
|
||
$response = Http::get($url);
|
||
if ($response->failed()) {
|
||
throw new Exception("Impossibile scaricare il documento catastale dall'URL: {$url}");
|
||
}
|
||
|
||
$content = $response->body();
|
||
$dirPath = $this->getStoragePath($stabileId);
|
||
Storage::disk('public')->makeDirectory($dirPath);
|
||
|
||
$localRelativePath = "{$dirPath}/{$targetFilename}";
|
||
|
||
// Se il file scaricato è uno ZIP (es. in caso di omoimmobili), lo scompattiamo
|
||
if (str_starts_with($content, "PK\x03\x04")) {
|
||
$tempZipPath = tempnam(sys_get_temp_dir(), 'sister_zip_');
|
||
file_put_contents($tempZipPath, $content);
|
||
|
||
$zip = new ZipArchive();
|
||
if ($zip->open($tempZipPath) === true) {
|
||
$firstFile = $zip->numFiles > 0 ? $zip->getNameIndex(0) : '';
|
||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||
$filename = $zip->getNameIndex($i);
|
||
$fileContent = $zip->getFromIndex($i);
|
||
Storage::disk('public')->put("{$dirPath}/{$filename}", $fileContent);
|
||
}
|
||
$zip->close();
|
||
unlink($tempZipPath);
|
||
|
||
return [
|
||
'status' => 'success',
|
||
'zip_extracted' => true,
|
||
'directory' => $dirPath,
|
||
'file_path' => "{$dirPath}/" . $firstFile,
|
||
'url' => Storage::disk('public')->url("{$dirPath}/" . $firstFile),
|
||
];
|
||
}
|
||
unlink($tempZipPath);
|
||
}
|
||
|
||
Storage::disk('public')->put($localRelativePath, $content);
|
||
|
||
return [
|
||
'status' => 'success',
|
||
'cached' => false,
|
||
'file_path' => $localRelativePath,
|
||
'url' => Storage::disk('public')->url($localRelativePath),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Esegue il polling per le richieste in evasione differita.
|
||
*/
|
||
protected function pollRichiestaEvasione(int $stabileId, int $idRichiesta, string $targetFilename): array
|
||
{
|
||
$creds = $this->resolveCredentials($stabileId);
|
||
$attempts = 0;
|
||
$maxAttempts = 5;
|
||
|
||
while ($attempts < $maxAttempts) {
|
||
$attempts++;
|
||
sleep(5);
|
||
|
||
$params = [
|
||
'passwordScript' => $creds['passwordScript'],
|
||
'app_url' => $creds['app_url'],
|
||
'download' => $idRichiesta,
|
||
];
|
||
|
||
$url = str_replace('ScaricaFatture/CassettoFiscale.php', 'sister/elencoRichieste.php', $creds['base_url']);
|
||
$url = str_replace('FattureCorrispettivi/sister/', 'sister/', $url);
|
||
|
||
$response = Http::asForm()->post($url, $params);
|
||
|
||
if ($response->successful()) {
|
||
$content = $response->body();
|
||
if (!str_starts_with(trim($content), '{')) {
|
||
$dirPath = $this->getStoragePath($stabileId);
|
||
Storage::disk('public')->makeDirectory($dirPath);
|
||
$localRelativePath = "{$dirPath}/{$targetFilename}";
|
||
Storage::disk('public')->put($localRelativePath, $content);
|
||
|
||
return [
|
||
'status' => 'success',
|
||
'cached' => false,
|
||
'file_path' => $localRelativePath,
|
||
'url' => Storage::disk('public')->url($localRelativePath),
|
||
];
|
||
}
|
||
|
||
$json = json_decode($content, true);
|
||
if (isset($json['err']) && !str_contains($json['err'], 'in produzione') && !str_contains($json['err'], 'in evasione')) {
|
||
throw new Exception("Errore nel polling Sister: " . $json['err']);
|
||
}
|
||
}
|
||
}
|
||
|
||
throw new Exception("Tempo massimo scaduto per l'evasione catastale. idRichiesta: {$idRichiesta}");
|
||
}
|
||
|
||
/**
|
||
* Recupera dinamicamente il parametro denomComune interrogando comuni.php.
|
||
*/
|
||
public function getComuneCat(int $stabileId, string $uffProvinciale, string $cittaName): string
|
||
{
|
||
$creds = $this->resolveCredentials($stabileId);
|
||
|
||
$url = str_replace('ScaricaFatture/CassettoFiscale.php', 'sister/comuni.php', $creds['base_url']);
|
||
$url = str_replace('FattureCorrispettivi/sister/', 'sister/', $url);
|
||
|
||
try {
|
||
$response = Http::get($url, [
|
||
'uffprovinciale' => $uffProvinciale
|
||
]);
|
||
|
||
if ($response->successful()) {
|
||
$comuni = $response->json();
|
||
if (is_array($comuni)) {
|
||
foreach ($comuni as $c) {
|
||
if (strcasecmp(trim($c['comune'] ?? ''), trim($cittaName)) === 0) {
|
||
$cod = $c['codice'] ?? '';
|
||
$sez = $c['sezioni'] ?? '0';
|
||
$prov = $c['prov'] ?? '0';
|
||
return "{$cod}#{$cittaName}#{$sez}#{$prov}";
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (Exception $e) {}
|
||
|
||
// Fallback robusto
|
||
$comuneCod = (strtoupper($cittaName) === 'ROMA') ? 'H501' : 'H501';
|
||
return "{$comuneCod}#{$cittaName}#0#0";
|
||
}
|
||
}
|