738 lines
25 KiB
PHP
738 lines
25 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Import;
|
|
|
|
use App\Models\GestioneContabile;
|
|
use App\Models\OperazioneContabile;
|
|
use App\Models\RegistroRitenuteAcconto;
|
|
use App\Models\IncassoEstrattoConto;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* MULTI-YEAR GESCON IMPORT SERVICE
|
|
*
|
|
* Gestisce import diretto da file MDB multipli con:
|
|
* - Separazione gestioni per anno
|
|
* - Continuità contabile multi-anno
|
|
* - Partita doppia con NATURA2
|
|
* - Tracciabilità modifiche
|
|
* - Registro ritenute d'acconto
|
|
*/
|
|
class MultiYearGesconImportService
|
|
{
|
|
private string $tenantId;
|
|
private array $mdbConnections = [];
|
|
private array $gestioni = [];
|
|
|
|
public function __construct(string $tenantId)
|
|
{
|
|
$this->tenantId = $tenantId;
|
|
}
|
|
|
|
/**
|
|
* Import completo multi-anno da directory Gescon
|
|
*/
|
|
public function importFromGesconDirectory(string $baseDir): array
|
|
{
|
|
Log::info("Starting multi-year Gescon import", ['tenant' => $this->tenantId, 'dir' => $baseDir]);
|
|
|
|
// 1. Scansiona directory per anni
|
|
$years = $this->scanGesconYears($baseDir);
|
|
|
|
// 2. Crea gestioni contabili per ogni anno
|
|
$this->createGestioni($years);
|
|
|
|
// 3. Import dati per ogni gestione
|
|
$results = [];
|
|
foreach ($years as $year => $paths) {
|
|
$results[$year] = $this->importGestioneYear($year, $paths);
|
|
}
|
|
|
|
// 4. Crea bilanci di apertura per continuità
|
|
$this->createBilanciApertura();
|
|
|
|
return [
|
|
'success' => true,
|
|
'gestioni_create' => count($this->gestioni),
|
|
'years_imported' => array_keys($results),
|
|
'details' => $results
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Scansiona directory Gescon per identificare anni
|
|
*/
|
|
private function scanGesconYears(string $baseDir): array
|
|
{
|
|
$years = [];
|
|
|
|
// Pattern directory: /Gescon/2024/singolo_anno.mdb, /Gescon/2025/singolo_anno.mdb
|
|
$gesconDirs = glob($baseDir . '/*/');
|
|
|
|
foreach ($gesconDirs as $dir) {
|
|
$year = basename(rtrim($dir, '/'));
|
|
if (is_numeric($year) && strlen($year) == 4) {
|
|
$paths = [
|
|
'singolo_anno' => $dir . 'singolo_anno.mdb',
|
|
'generale_stabile' => $dir . 'generale_stabile.mdb'
|
|
];
|
|
|
|
// Verifica esistenza file
|
|
if (file_exists($paths['singolo_anno'])) {
|
|
$years[$year] = $paths;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $years;
|
|
}
|
|
|
|
/**
|
|
* Scansiona gestioni direttamente dal file MDB
|
|
*/
|
|
private function scanGestioniFromMdb(string $mdbPath, int $year): array
|
|
{
|
|
$gestioni = [];
|
|
|
|
// Gestione Ordinaria (sempre presente)
|
|
$gestioni[] = [
|
|
'anno_gestione' => $year,
|
|
'tipo_gestione' => 'ordinaria',
|
|
'denominazione' => "Gestione Ordinaria {$year}",
|
|
'data_inizio' => "{$year}-01-01",
|
|
'data_fine' => "{$year}-12-31",
|
|
'protocollo_prefix' => "O{$year}"
|
|
];
|
|
|
|
// Gestione Riscaldamento (se presente)
|
|
$opRiscaldamento = $this->queryMdb($mdbPath, "
|
|
SELECT COUNT(*) as cnt FROM operazioni
|
|
WHERE gestione = 'R' OR gestione = 'r'
|
|
");
|
|
|
|
if (!empty($opRiscaldamento) && ($opRiscaldamento[0]['cnt'] ?? 0) > 0) {
|
|
$gestioni[] = [
|
|
'anno_gestione' => $year,
|
|
'tipo_gestione' => 'riscaldamento',
|
|
'denominazione' => "Gestione Riscaldamento {$year}",
|
|
'data_inizio' => "{$year}-01-01",
|
|
'data_fine' => "{$year}-12-31",
|
|
'protocollo_prefix' => "R{$year}"
|
|
];
|
|
}
|
|
|
|
// Gestioni Straordinarie (dinamiche)
|
|
$straordinarie = $this->queryMdb($mdbPath, "
|
|
SELECT DISTINCT N_STRA as numero
|
|
FROM operazioni
|
|
WHERE (gestione = 'S' OR gestione = 's')
|
|
AND N_STRA IS NOT NULL
|
|
AND N_STRA > 0
|
|
ORDER BY N_STRA
|
|
");
|
|
|
|
foreach ($straordinarie as $stra) {
|
|
$numero = (int)$stra['numero'];
|
|
$gestioni[] = [
|
|
'anno_gestione' => $year,
|
|
'tipo_gestione' => 'straordinaria',
|
|
'numero_straordinaria' => $numero,
|
|
'denominazione' => "Gestione Straordinaria {$year} - {$numero}",
|
|
'data_inizio' => "{$year}-01-01",
|
|
'data_fine' => "{$year}-12-31",
|
|
'protocollo_prefix' => "S{$year}-{$numero}"
|
|
];
|
|
}
|
|
|
|
return $gestioni;
|
|
}
|
|
|
|
/**
|
|
* Crea o aggiorna gestione
|
|
*/
|
|
private function createOrUpdateGestione(array $gestioneData): GestioneContabile
|
|
{
|
|
$gestione = GestioneContabile::updateOrCreate([
|
|
'tenant_id' => $this->tenantId,
|
|
'anno_gestione' => $gestioneData['anno_gestione'],
|
|
'tipo_gestione' => $gestioneData['tipo_gestione'],
|
|
'numero_straordinaria' => $gestioneData['numero_straordinaria'] ?? null
|
|
], [
|
|
'denominazione' => $gestioneData['denominazione'],
|
|
'data_inizio' => $gestioneData['data_inizio'],
|
|
'data_fine' => $gestioneData['data_fine'],
|
|
'stato' => 'aperta',
|
|
'protocollo_prefix' => $gestioneData['protocollo_prefix']
|
|
]);
|
|
|
|
// Cache per lookup veloce
|
|
$year = $gestioneData['anno_gestione'];
|
|
$tipo = $gestioneData['tipo_gestione'];
|
|
|
|
if ($tipo === 'straordinaria') {
|
|
$key = "straordinaria_{$gestioneData['numero_straordinaria']}";
|
|
} else {
|
|
$key = $tipo;
|
|
}
|
|
|
|
$this->gestioni[$year][$key] = $gestione;
|
|
|
|
return $gestione;
|
|
}
|
|
|
|
/**
|
|
* Import operazioni per gestione specifica
|
|
*/
|
|
private function importOperazioniForGestione(GestioneContabile $gestione, string $mdbPath): int
|
|
{
|
|
// Filtro per tipo gestione
|
|
$whereClause = $this->buildGestioneWhereClause($gestione);
|
|
|
|
$operazioni = $this->queryMdb($mdbPath, "
|
|
SELECT *,
|
|
IIF(ISNULL(COMPET), 'C', COMPET) as compet_clean,
|
|
IIF(ISNULL(NATURA2), '', NATURA2) as natura2_clean,
|
|
IIF(ISNULL(N_STRA), 0, N_STRA) as n_stra_clean
|
|
FROM operazioni
|
|
WHERE {$whereClause}
|
|
ORDER BY n_operazione
|
|
");
|
|
|
|
$imported = 0;
|
|
foreach ($operazioni as $op) {
|
|
try {
|
|
$protocollo = $this->generateProtocollo($gestione->id, $op);
|
|
$voceSnapshot = $this->getVoceSpesaSnapshot($mdbPath, $op['cod_voce'] ?? '');
|
|
|
|
OperazioneContabile::create([
|
|
'tenant_id' => $this->tenantId,
|
|
'gestione_id' => $gestione->id,
|
|
'numero_operazione' => $op['n_operazione'],
|
|
'data_operazione' => $this->parseGesconDate($op['data_oper']),
|
|
'importo' => (float)($op['euro'] ?? 0),
|
|
'descrizione' => $op['descrizione'] ?? '',
|
|
'cod_voce_gescon' => $op['cod_voce'] ?? '',
|
|
'cod_beneficiario_gescon' => $op['cod_ben'] ?? '',
|
|
'compet' => $op['compet_clean'],
|
|
'natura2' => $op['natura2_clean'],
|
|
'n_stra' => (int)$op['n_stra_clean'],
|
|
'protocollo_numero' => $protocollo['numero'],
|
|
'protocollo_completo' => $protocollo['completo'],
|
|
'voce_spesa_snapshot' => json_encode($voceSnapshot),
|
|
'metadati_gescon' => json_encode($op)
|
|
]);
|
|
|
|
$imported++;
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("Error importing operazione for gestione", [
|
|
'gestione_id' => $gestione->id,
|
|
'operazione' => $op,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $imported;
|
|
}
|
|
|
|
/**
|
|
* Import incassi per gestione
|
|
*/
|
|
private function importIncassiForGestione(GestioneContabile $gestione, string $mdbPath): int
|
|
{
|
|
$whereClause = $this->buildGestioneWhereClause($gestione);
|
|
|
|
$incassi = $this->queryMdb($mdbPath, "
|
|
SELECT * FROM incassi
|
|
WHERE {$whereClause}
|
|
ORDER BY data_inc
|
|
");
|
|
|
|
$imported = 0;
|
|
foreach ($incassi as $inc) {
|
|
try {
|
|
// Import incasso...
|
|
$imported++;
|
|
} catch (\Exception $e) {
|
|
Log::error("Error importing incasso", [
|
|
'gestione_id' => $gestione->id,
|
|
'incasso' => $inc,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $imported;
|
|
}
|
|
|
|
/**
|
|
* Import ritenute per gestione
|
|
*/
|
|
private function importRitenuteFOrGestione(GestioneContabile $gestione, string $mdbPath): int
|
|
{
|
|
$ritenute = $this->queryMdb($mdbPath, "
|
|
SELECT n.*, o.data_oper, o.euro as imponibile_operazione
|
|
FROM Nettovers_RDA n
|
|
LEFT JOIN operazioni o ON n.Rif_RDA = o.n_operazione
|
|
WHERE n.Rif_RDA IS NOT NULL
|
|
AND {$this->buildGestioneJoinClause($gestione, 'o')}
|
|
ORDER BY n.Rif_RDA
|
|
");
|
|
|
|
$imported = 0;
|
|
foreach ($ritenute as $ra) {
|
|
try {
|
|
RegistroRitenuteAcconto::create([
|
|
'tenant_id' => $this->tenantId,
|
|
'gestione_id' => $gestione->id,
|
|
'numero_progressivo' => $ra['Rif_RDA'],
|
|
'data_competenza' => $this->parseGesconDate($ra['data_oper']),
|
|
'imponibile' => (float)($ra['imponibile_operazione'] ?? 0),
|
|
'aliquota_ritenuta' => (float)($ra['aliquota'] ?? 20),
|
|
'importo_ritenuta' => (float)($ra['ritenuta'] ?? 0),
|
|
'rif_rda' => $ra['Rif_RDA'],
|
|
'stato_versamento' => 'da_versare',
|
|
'metadati_gescon' => json_encode($ra)
|
|
]);
|
|
|
|
$imported++;
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("Error importing ritenuta", [
|
|
'gestione_id' => $gestione->id,
|
|
'ritenuta' => $ra,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $imported;
|
|
}
|
|
|
|
/**
|
|
* Import incassi estratto conto per gestione
|
|
*/
|
|
private function importIncassiEstrattoContoForGestione(GestioneContabile $gestione, string $mdbPath): int
|
|
{
|
|
$incassiEc = $this->queryMdb($mdbPath, "
|
|
SELECT * FROM Inc_da_ec
|
|
ORDER BY data_oper
|
|
");
|
|
|
|
$imported = 0;
|
|
foreach ($incassiEc as $inc) {
|
|
try {
|
|
IncassoEstrattoConto::create([
|
|
'tenant_id' => $this->tenantId,
|
|
'gestione_id' => $gestione->id,
|
|
'id_gescon' => $inc['id'] ?? null,
|
|
'data_operazione' => $this->parseGesconDate($inc['data_oper']),
|
|
'data_valuta' => $this->parseGesconDate($inc['data_valuta']),
|
|
'importo' => (float)($inc['importo'] ?? 0),
|
|
'descrizione' => $inc['descrizione'] ?? '',
|
|
'stato' => 'da_riconciliare',
|
|
'metadati_gescon' => json_encode($inc)
|
|
]);
|
|
|
|
$imported++;
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("Error importing incasso EC", [
|
|
'gestione_id' => $gestione->id,
|
|
'incasso' => $inc,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $imported;
|
|
}
|
|
|
|
/**
|
|
* Costruisce WHERE clause per filtrare per gestione
|
|
*/
|
|
private function buildGestioneWhereClause(GestioneContabile $gestione): string
|
|
{
|
|
switch ($gestione->tipo_gestione) {
|
|
case 'ordinaria':
|
|
return "(gestione = 'O' OR gestione = 'o' OR gestione IS NULL OR gestione = '')";
|
|
|
|
case 'riscaldamento':
|
|
return "(gestione = 'R' OR gestione = 'r')";
|
|
|
|
case 'straordinaria':
|
|
$numero = $gestione->numero_straordinaria;
|
|
return "(gestione = 'S' OR gestione = 's') AND N_STRA = {$numero}";
|
|
|
|
default:
|
|
return "1=1";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Costruisce JOIN clause per filtrare per gestione
|
|
*/
|
|
private function buildGestioneJoinClause(GestioneContabile $gestione, string $alias = ''): string
|
|
{
|
|
$prefix = $alias ? "{$alias}." : '';
|
|
|
|
switch ($gestione->tipo_gestione) {
|
|
case 'ordinaria':
|
|
return "({$prefix}gestione = 'O' OR {$prefix}gestione = 'o' OR {$prefix}gestione IS NULL OR {$prefix}gestione = '')";
|
|
|
|
case 'riscaldamento':
|
|
return "({$prefix}gestione = 'R' OR {$prefix}gestione = 'r')";
|
|
|
|
case 'straordinaria':
|
|
$numero = $gestione->numero_straordinaria;
|
|
return "({$prefix}gestione = 'S' OR {$prefix}gestione = 's') AND {$prefix}N_STRA = {$numero}";
|
|
|
|
default:
|
|
return "1=1";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Import dati per una specifica gestione/anno
|
|
*/
|
|
private function importGestioneYear(string $year, array $paths): array
|
|
{
|
|
$results = [
|
|
'operazioni' => 0,
|
|
'incassi' => 0,
|
|
'ritenute' => 0,
|
|
'rate_emesse' => 0,
|
|
'incassi_ec' => 0
|
|
];
|
|
|
|
// 1. Import operazioni contabili
|
|
$results['operazioni'] = $this->importOperazioni($year, $paths['singolo_anno']);
|
|
|
|
// 2. Import incassi
|
|
$results['incassi'] = $this->importIncassi($year, $paths['singolo_anno']);
|
|
|
|
// 3. Import ritenute d'acconto
|
|
$results['ritenute'] = $this->importRitenuteAcconto($year, $paths['singolo_anno']);
|
|
|
|
// 4. Import rate emesse (da generale_stabile.mdb)
|
|
if (file_exists($paths['generale_stabile'])) {
|
|
$results['rate_emesse'] = $this->importRateEmesse($year, $paths['generale_stabile']);
|
|
}
|
|
|
|
// 5. Import incassi estratto conto
|
|
$results['incassi_ec'] = $this->importIncassiEstrattoConto($year, $paths['singolo_anno']);
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Import operazioni con gestione COMPET e NATURA2
|
|
*/
|
|
private function importOperazioni(string $year, string $mdbPath): int
|
|
{
|
|
$imported = 0;
|
|
|
|
// Query MDB per operazioni
|
|
$operazioni = $this->queryMdb($mdbPath, "
|
|
SELECT *,
|
|
IIF(ISNULL(COMPET), 'C', COMPET) as compet_clean,
|
|
IIF(ISNULL(NATURA2), '', NATURA2) as natura2_clean,
|
|
IIF(ISNULL(N_STRA), 0, N_STRA) as n_stra_clean
|
|
FROM operazioni
|
|
ORDER BY n_operazione
|
|
");
|
|
|
|
foreach ($operazioni as $op) {
|
|
try {
|
|
// Determina gestione
|
|
$gestioneId = $this->determineGestioneId($year, $op);
|
|
|
|
// Genera protocollo
|
|
$protocollo = $this->generateProtocollo($gestioneId, $op);
|
|
|
|
// Snapshot voce spesa
|
|
$voceSnapshot = $this->getVoceSpesaSnapshot($mdbPath, $op['cod_voce'] ?? '');
|
|
|
|
OperazioneContabile::create([
|
|
'tenant_id' => $this->tenantId,
|
|
'gestione_id' => $gestioneId,
|
|
'numero_operazione' => $op['n_operazione'],
|
|
'data_operazione' => $this->parseGesconDate($op['data_oper']),
|
|
'importo' => (float)($op['euro'] ?? 0),
|
|
'descrizione' => $op['descrizione'] ?? '',
|
|
'cod_voce_gescon' => $op['cod_voce'] ?? '',
|
|
'cod_beneficiario_gescon' => $op['cod_ben'] ?? '',
|
|
'compet' => $op['compet_clean'],
|
|
'natura2' => $op['natura2_clean'],
|
|
'n_stra' => (int)$op['n_stra_clean'],
|
|
'protocollo_numero' => $protocollo['numero'],
|
|
'protocollo_completo' => $protocollo['completo'],
|
|
'voce_spesa_snapshot' => json_encode($voceSnapshot),
|
|
'metadati_gescon' => json_encode($op)
|
|
]);
|
|
|
|
$imported++;
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("Error importing operazione", [
|
|
'year' => $year,
|
|
'operazione' => $op,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $imported;
|
|
}
|
|
|
|
/**
|
|
* Import ritenute d'acconto da Nettovers_RDA
|
|
*/
|
|
private function importRitenuteAcconto(string $year, string $mdbPath): int
|
|
{
|
|
$imported = 0;
|
|
|
|
// Query per ritenute
|
|
$ritenute = $this->queryMdb($mdbPath, "
|
|
SELECT n.*, o.data_oper, o.euro as imponibile_operazione
|
|
FROM Nettovers_RDA n
|
|
LEFT JOIN operazioni o ON n.Rif_RDA = o.n_operazione
|
|
WHERE n.Rif_RDA IS NOT NULL
|
|
ORDER BY n.Rif_RDA
|
|
");
|
|
|
|
foreach ($ritenute as $ra) {
|
|
try {
|
|
$gestioneId = $this->gestioni[$year]['ordinaria']->id;
|
|
|
|
RegistroRitenuteAcconto::create([
|
|
'tenant_id' => $this->tenantId,
|
|
'gestione_id' => $gestioneId,
|
|
'numero_progressivo' => $ra['Rif_RDA'],
|
|
'data_competenza' => $this->parseGesconDate($ra['data_oper']),
|
|
'imponibile' => (float)($ra['imponibile_operazione'] ?? 0),
|
|
'aliquota_ritenuta' => (float)($ra['aliquota'] ?? 20),
|
|
'importo_ritenuta' => (float)($ra['ritenuta'] ?? 0),
|
|
'rif_rda' => $ra['Rif_RDA'],
|
|
'stato_versamento' => 'da_versare', // Default
|
|
'metadati_gescon' => json_encode($ra)
|
|
]);
|
|
|
|
$imported++;
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("Error importing ritenuta", [
|
|
'year' => $year,
|
|
'ritenuta' => $ra,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $imported;
|
|
}
|
|
|
|
/**
|
|
* Import incassi estratto conto da Inc_da_ec
|
|
*/
|
|
private function importIncassiEstrattoConto(string $year, string $mdbPath): int
|
|
{
|
|
$imported = 0;
|
|
|
|
$incassiEc = $this->queryMdb($mdbPath, "
|
|
SELECT * FROM Inc_da_ec
|
|
ORDER BY data_oper
|
|
");
|
|
|
|
foreach ($incassiEc as $inc) {
|
|
try {
|
|
$gestioneId = $this->gestioni[$year]['ordinaria']->id;
|
|
|
|
IncassoEstrattoConto::create([
|
|
'tenant_id' => $this->tenantId,
|
|
'gestione_id' => $gestioneId,
|
|
'id_gescon' => $inc['id'] ?? null,
|
|
'data_operazione' => $this->parseGesconDate($inc['data_oper']),
|
|
'data_valuta' => $this->parseGesconDate($inc['data_valuta']),
|
|
'importo' => (float)($inc['importo'] ?? 0),
|
|
'descrizione' => $inc['descrizione'] ?? '',
|
|
'stato' => 'da_riconciliare',
|
|
'metadati_gescon' => json_encode($inc)
|
|
]);
|
|
|
|
$imported++;
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("Error importing incasso EC", [
|
|
'year' => $year,
|
|
'incasso' => $inc,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $imported;
|
|
}
|
|
|
|
/**
|
|
* Import rate emesse da generale_stabile.mdb
|
|
*/
|
|
private function importRateEmesse(string $year, string $mdbPath): int
|
|
{
|
|
$imported = 0;
|
|
|
|
// Unifica EMESS_DET, EMESS_DET_2, EMESS_GEN
|
|
$tables = ['EMESS_DET', 'EMESS_DET_2', 'EMESS_GEN'];
|
|
|
|
foreach ($tables as $table) {
|
|
$rate = $this->queryMdb($mdbPath, "SELECT * FROM {$table} ORDER BY data_em");
|
|
|
|
foreach ($rate as $rata) {
|
|
try {
|
|
// Logica import rate...
|
|
$imported++;
|
|
} catch (\Exception $e) {
|
|
Log::error("Error importing rata", ['table' => $table, 'rata' => $rata, 'error' => $e->getMessage()]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $imported;
|
|
}
|
|
|
|
/**
|
|
* Determina gestione ID basata su dati operazione
|
|
*/
|
|
private function determineGestioneId(string $year, array $operazione): int
|
|
{
|
|
$gestione = $operazione['gestione'] ?? 'O';
|
|
$nStra = (int)($operazione['N_STRA'] ?? 0);
|
|
|
|
switch (strtoupper($gestione)) {
|
|
case 'R':
|
|
return $this->gestioni[$year]['riscaldamento']->id;
|
|
|
|
case 'S':
|
|
// Crea gestione straordinaria se non esiste
|
|
return $this->getOrCreateStraordinaria($year, $nStra);
|
|
|
|
default:
|
|
return $this->gestioni[$year]['ordinaria']->id;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ottiene o crea gestione straordinaria
|
|
*/
|
|
private function getOrCreateStraordinaria(string $year, int $nStra): int
|
|
{
|
|
$key = "straordinaria_{$nStra}";
|
|
|
|
if (!isset($this->gestioni[$year][$key])) {
|
|
$this->gestioni[$year][$key] = GestioneContabile::create([
|
|
'tenant_id' => $this->tenantId,
|
|
'anno_gestione' => (int)$year,
|
|
'tipo_gestione' => 'straordinaria',
|
|
'numero_straordinaria' => $nStra,
|
|
'denominazione' => "Gestione Straordinaria {$year} - {$nStra}",
|
|
'data_inizio' => "{$year}-01-01",
|
|
'data_fine' => "{$year}-12-31",
|
|
'stato' => 'aperta',
|
|
'protocollo_prefix' => "S{$year}-{$nStra}"
|
|
]);
|
|
}
|
|
|
|
return $this->gestioni[$year][$key]->id;
|
|
}
|
|
|
|
/**
|
|
* Query diretta su file MDB
|
|
*/
|
|
private function queryMdb(string $mdbPath, string $query): array
|
|
{
|
|
// Usa mdb-tools per query diretta
|
|
$command = sprintf(
|
|
'mdb-sql "%s" <<< "%s"',
|
|
escapeshellarg($mdbPath),
|
|
escapeshellarg($query)
|
|
);
|
|
|
|
$output = shell_exec($command);
|
|
|
|
// Parser output MDB in array associativo
|
|
return $this->parseMdbOutput($output);
|
|
}
|
|
|
|
/**
|
|
* Parser output mdb-sql in array
|
|
*/
|
|
private function parseMdbOutput(string $output): array
|
|
{
|
|
$lines = explode("\n", trim($output));
|
|
if (empty($lines)) return [];
|
|
|
|
$headers = array_map('trim', explode("\t", array_shift($lines)));
|
|
$data = [];
|
|
|
|
foreach ($lines as $line) {
|
|
if (empty(trim($line))) continue;
|
|
$values = explode("\t", $line);
|
|
$row = array_combine($headers, $values);
|
|
$data[] = $row;
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Genera protocollo per operazione
|
|
*/
|
|
private function generateProtocollo(int $gestioneId, array $operazione): array
|
|
{
|
|
$gestione = GestioneContabile::find($gestioneId);
|
|
$numero = $gestione->getNextProtocolNumber();
|
|
|
|
return [
|
|
'numero' => $numero,
|
|
'completo' => $gestione->protocollo_prefix . '-' . str_pad($numero, 4, '0', STR_PAD_LEFT)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Snapshot voce spesa
|
|
*/
|
|
private function getVoceSpesaSnapshot(string $mdbPath, string $codVoce): array
|
|
{
|
|
if (empty($codVoce)) return [];
|
|
|
|
$voci = $this->queryMdb($mdbPath, "
|
|
SELECT * FROM voci_spesa
|
|
WHERE cod_voce = '{$codVoce}'
|
|
");
|
|
|
|
return $voci[0] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Parse data Gescon
|
|
*/
|
|
private function parseGesconDate($dateValue): ?string
|
|
{
|
|
if (empty($dateValue)) return null;
|
|
|
|
// Prova vari formati date Gescon/Access
|
|
$formats = ['Y-m-d', 'd/m/Y', 'm/d/Y', 'Y-m-d H:i:s'];
|
|
|
|
foreach ($formats as $format) {
|
|
$date = \DateTime::createFromFormat($format, $dateValue);
|
|
if ($date !== false) {
|
|
return $date->format('Y-m-d');
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|