diff --git a/app/Models/PianoRateizzazione.php b/app/Models/PianoRateizzazione.php index 302a9388..85727922 100644 --- a/app/Models/PianoRateizzazione.php +++ b/app/Models/PianoRateizzazione.php @@ -104,6 +104,14 @@ class PianoRateizzazione extends Model return $this->belongsTo(User::class, 'attivato_da'); } + /** + * Relazione con RipartizioneSpese + */ + public function ripartizione() + { + return $this->belongsTo(RipartizioneSpese::class, 'ripartizione_spese_id'); + } + /** * Relazione con Rate */ diff --git a/app/Models/Rata.php b/app/Models/Rata.php index 4038592d..917bbf42 100644 --- a/app/Models/Rata.php +++ b/app/Models/Rata.php @@ -105,6 +105,14 @@ class Rata extends Model return $this->belongsTo(PianoRateizzazione::class); } + /** + * Relazione con RipartizioneSpese + */ + public function ripartizione() + { + return $this->belongsTo(RipartizioneSpese::class, 'ripartizione_spese_id'); + } + /** * Relazione con UnitaImmobiliare */ diff --git a/app/helpers.php b/app/helpers.php new file mode 100644 index 00000000..01da63fe --- /dev/null +++ b/app/helpers.php @@ -0,0 +1,68 @@ +check()) { + return $default; + } + + $user = auth()->user(); + + // Se l'utente ha un campo settings + if (isset($user->settings) && is_array($user->settings)) { + return $user->settings[$key] ?? $default; + } + + // Se l'utente ha una relazione settings + if (method_exists($user, 'settings')) { + $setting = $user->settings()->where('key', $key)->first(); + return $setting ? $setting->value : $default; + } + + return $default; + } +} + +if (!function_exists('setUserSetting')) { + /** + * Imposta un'impostazione utente + * + * @param string $key + * @param mixed $value + * @return bool + */ + function setUserSetting($key, $value) + { + if (!auth()->check()) { + return false; + } + + $user = auth()->user(); + + // Se l'utente ha un campo settings + if (isset($user->settings)) { + $settings = is_array($user->settings) ? $user->settings : []; + $settings[$key] = $value; + $user->settings = $settings; + return $user->save(); + } + + // Se l'utente ha una relazione settings + if (method_exists($user, 'settings')) { + return $user->settings()->updateOrCreate( + ['key' => $key], + ['value' => $value] + ); + } + + return false; + } +} diff --git a/composer.json b/composer.json index a976a374..2600e13a 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "App\\": "app/" }, "files": [ - "app/Helpers/impostazioni.php" + "app/Helpers/impostazioni.php", + "app/helpers.php" ] }, "autoload-dev": { diff --git a/database/migrations/2025_07_07_223229_enhance_unita_immobiliari_structure.php b/database/migrations/2025_07_07_223229_enhance_unita_immobiliari_structure.php index 5beafb38..48cbe518 100644 --- a/database/migrations/2025_07_07_223229_enhance_unita_immobiliari_structure.php +++ b/database/migrations/2025_07_07_223229_enhance_unita_immobiliari_structure.php @@ -55,15 +55,37 @@ return new class extends Migration public function down(): void { Schema::table('unita_immobiliari', function (Blueprint $table) { - $table->dropIndex(['idx_unita_stabile_stato']); - $table->dropIndex(['idx_unita_tipo_utilizzo']); - $table->dropColumn([ - 'palazzina', 'tipo_utilizzo_id', 'millesimi_proprieta', - 'rendita_catastale', 'codice_univoco', 'stato' - ]); + // Rimuove gli indici solo se esistono - con controllo sicuro + try { + $table->dropIndex(['stabile_id', 'stato']); + } catch (\Exception $e) { + // Indice non esistente, ignora + } - if (Schema::hasColumn('unita_immobiliari', 'palazzina_old')) { - $table->renameColumn('palazzina_old', 'fabbricato'); + try { + $table->dropIndex(['tipo_utilizzo_id']); + } catch (\Exception $e) { + // Indice non esistente, ignora + } + + // Rimuove le colonne se esistono + if (Schema::hasColumn('unita_immobiliari', 'palazzina')) { + $table->dropColumn('palazzina'); + } + if (Schema::hasColumn('unita_immobiliari', 'tipo_utilizzo_id')) { + $table->dropColumn('tipo_utilizzo_id'); + } + if (Schema::hasColumn('unita_immobiliari', 'millesimi_proprieta')) { + $table->dropColumn('millesimi_proprieta'); + } + if (Schema::hasColumn('unita_immobiliari', 'rendita_catastale')) { + $table->dropColumn('rendita_catastale'); + } + if (Schema::hasColumn('unita_immobiliari', 'codice_univoco')) { + $table->dropColumn('codice_univoco'); + } + if (Schema::hasColumn('unita_immobiliari', 'stato')) { + $table->dropColumn('stato'); } }); } diff --git a/database/migrations/2025_07_07_223601_create_anagrafica_condominiale_table.php b/database/migrations/2025_07_07_223601_create_anagrafica_condominiale_table.php index cf7edf9d..cbf15668 100644 --- a/database/migrations/2025_07_07_223601_create_anagrafica_condominiale_table.php +++ b/database/migrations/2025_07_07_223601_create_anagrafica_condominiale_table.php @@ -21,7 +21,7 @@ return new class extends Migration $table->string('cognome')->nullable()->comment('Cognome (se persona fisica)'); $table->string('nome')->nullable()->comment('Nome (se persona fisica)'); $table->string('denominazione')->nullable()->comment('Denominazione (se persona giuridica)'); - $table->string('codice_fiscale', 16)->index()->comment('Codice fiscale'); + $table->string('codice_fiscale', 16)->comment('Codice fiscale'); $table->string('partita_iva', 11)->nullable()->comment('Partita IVA (se presente)'); // Dati nascita (solo per persone fisiche) diff --git a/database/migrations/2025_07_08_063341_create_contratti_locazione_table.php b/database/migrations/2025_07_08_063341_create_contratti_locazione_table.php index a8ec5581..fd0ea553 100644 --- a/database/migrations/2025_07_08_063341_create_contratti_locazione_table.php +++ b/database/migrations/2025_07_08_063341_create_contratti_locazione_table.php @@ -13,9 +13,9 @@ return new class extends Migration { Schema::create('contratti_locazione', function (Blueprint $table) { $table->id(); - $table->bigInteger('unita_immobiliare_id')->unsigned()->index()->comment('FK verso unita_immobiliari'); - $table->bigInteger('locatore_id')->unsigned()->index()->comment('FK verso anagrafica_condominiale (proprietario)'); - $table->bigInteger('conduttore_id')->unsigned()->index()->comment('FK verso anagrafica_condominiale (inquilino)'); + $table->bigInteger('unita_immobiliare_id')->unsigned()->comment('FK verso unita_immobiliari'); + $table->bigInteger('locatore_id')->unsigned()->comment('FK verso anagrafica_condominiale (proprietario)'); + $table->bigInteger('conduttore_id')->unsigned()->comment('FK verso anagrafica_condominiale (inquilino)'); $table->string('numero_contratto')->nullable()->comment('Numero identificativo contratto'); $table->date('data_contratto')->comment('Data stipula contratto'); diff --git a/database/migrations/2025_07_08_151345_create_ripartizione_spese_table.php b/database/migrations/2025_07_08_151345_create_ripartizione_spese_table.php index ab068435..987c66b2 100644 --- a/database/migrations/2025_07_08_151345_create_ripartizione_spese_table.php +++ b/database/migrations/2025_07_08_151345_create_ripartizione_spese_table.php @@ -13,7 +13,24 @@ return new class extends Migration { Schema::create('ripartizione_spese', function (Blueprint $table) { $table->id(); + $table->string('codice_ripartizione')->unique(); + $table->foreignId('voce_spesa_id')->constrained('voci_spesa')->cascadeOnDelete(); + $table->foreignId('stabile_id')->constrained('stabili')->cascadeOnDelete(); + $table->unsignedBigInteger('tabella_millesimale_id'); + $table->decimal('importo_totale', 10, 2); + $table->decimal('importo_ripartito', 10, 2)->default(0); + $table->string('tipo_ripartizione')->default('millesimale'); // millesimale, uguale, personalizzata + $table->date('data_ripartizione'); + $table->string('stato')->default('bozza'); // bozza, approvata, contabilizzata + $table->text('note')->nullable(); + $table->json('configurazione_ripartizione')->nullable(); + $table->foreignId('creato_da')->constrained('users'); + $table->timestamp('approvato_at')->nullable(); + $table->unsignedBigInteger('approvato_da')->nullable(); + $table->timestamp('contabilizzato_at')->nullable(); + $table->unsignedBigInteger('contabilizzato_da')->nullable(); $table->timestamps(); + $table->softDeletes(); }); } diff --git a/database/migrations/2025_07_08_151547_create_dettaglio_ripartizione_spese_table.php b/database/migrations/2025_07_08_151547_create_dettaglio_ripartizione_spese_table.php index 61aeff5f..d54875e2 100644 --- a/database/migrations/2025_07_08_151547_create_dettaglio_ripartizione_spese_table.php +++ b/database/migrations/2025_07_08_151547_create_dettaglio_ripartizione_spese_table.php @@ -13,7 +13,16 @@ return new class extends Migration { Schema::create('dettaglio_ripartizione_spese', function (Blueprint $table) { $table->id(); + $table->foreignId('ripartizione_spese_id')->constrained('ripartizione_spese')->cascadeOnDelete(); + $table->unsignedBigInteger('unita_immobiliare_id'); + $table->unsignedBigInteger('anagrafica_condominiale_id'); + $table->decimal('millesimi', 8, 3); + $table->decimal('importo_calcolato', 10, 2); + $table->decimal('importo_rettificato', 10, 2)->nullable(); + $table->string('motivo_rettifica')->nullable(); + $table->text('note')->nullable(); $table->timestamps(); + $table->softDeletes(); }); } diff --git a/database/migrations/2025_07_08_151654_create_rate_table.php b/database/migrations/2025_07_08_151654_create_rate_table.php index fa0afe45..a00997bf 100644 --- a/database/migrations/2025_07_08_151654_create_rate_table.php +++ b/database/migrations/2025_07_08_151654_create_rate_table.php @@ -13,7 +13,22 @@ return new class extends Migration { Schema::create('rate', function (Blueprint $table) { $table->id(); + $table->string('codice_rata')->unique(); + $table->foreignId('piano_rateizzazione_id')->constrained('piano_rateizzazione')->cascadeOnDelete(); + $table->foreignId('ripartizione_spese_id')->constrained('ripartizione_spese')->cascadeOnDelete(); + $table->integer('numero_rata'); + $table->decimal('importo_rata', 10, 2); + $table->date('data_scadenza'); + $table->string('stato')->default('attiva'); // attiva, pagata, scaduta, annullata + $table->date('data_pagamento')->nullable(); + $table->decimal('importo_pagato', 10, 2)->nullable(); + $table->string('modalita_pagamento')->nullable(); + $table->string('riferimento_pagamento')->nullable(); + $table->text('note')->nullable(); + $table->foreignId('registrato_da')->nullable()->constrained('users'); + $table->timestamp('registrato_at')->nullable(); $table->timestamps(); + $table->softDeletes(); }); } diff --git a/database/migrations/2025_07_08_151838_create_piano_rateizzazione_table.php b/database/migrations/2025_07_08_151838_create_piano_rateizzazione_table.php index 5d566655..22dc33ca 100644 --- a/database/migrations/2025_07_08_151838_create_piano_rateizzazione_table.php +++ b/database/migrations/2025_07_08_151838_create_piano_rateizzazione_table.php @@ -13,7 +13,23 @@ return new class extends Migration { Schema::create('piano_rateizzazione', function (Blueprint $table) { $table->id(); + $table->string('codice_piano')->unique(); + $table->foreignId('ripartizione_spese_id')->constrained('ripartizione_spese')->cascadeOnDelete(); + $table->foreignId('stabile_id')->constrained('stabili')->cascadeOnDelete(); + $table->string('descrizione'); + $table->string('tipo_piano')->default('standard'); // standard, personalizzato + $table->decimal('importo_totale', 10, 2); + $table->integer('numero_rate'); + $table->date('data_prima_rata'); + $table->string('frequenza')->default('mensile'); // mensile, bimestrale, trimestrale, semestrale + $table->string('stato')->default('attivo'); // attivo, sospeso, completato, annullato + $table->json('configurazione_rate')->nullable(); + $table->text('note')->nullable(); + $table->foreignId('creato_da')->constrained('users'); + $table->timestamp('attivato_at')->nullable(); + $table->foreignId('attivato_da')->nullable()->constrained('users'); $table->timestamps(); + $table->softDeletes(); }); } diff --git a/database/migrations/2025_07_08_223534_drop_ripartizione_tables.php b/database/migrations/2025_07_08_223534_drop_ripartizione_tables.php new file mode 100644 index 00000000..c1d1a0c5 --- /dev/null +++ b/database/migrations/2025_07_08_223534_drop_ripartizione_tables.php @@ -0,0 +1,27 @@ + +
| Codice: | +{{ $voceSpesa->codice_spesa }} |
+
| Stabile: | +{{ $voceSpesa->stabile->denominazione }} | +
| Categoria: | +
+ {{ $voceSpesa->categoria }}
+ @if($voceSpesa->sottocategoria)
+ {{ $voceSpesa->sottocategoria }} + @endif + |
+
| Importo Previsto: | ++ @if($voceSpesa->importo_previsto) + € {{ number_format($voceSpesa->importo_previsto, 2, ',', '.') }} + @else + Non specificato + @endif + | +
| Periodicità: | ++ @if($voceSpesa->periodicita) + {{ ucfirst(str_replace('_', ' ', $voceSpesa->periodicita)) }} + @else + Non specificata + @endif + | +
| Ripartizione Personalizzata: | ++ @if($voceSpesa->ripartizione_personalizzata) + Sì + @else + No + @endif + | +
| Tabella Millesimale Default: | +
+ {{ $voceSpesa->tabellaMillesimaleDefault->denominazione }}
+ {{ $voceSpesa->tabellaMillesimaleDefault->tipo_tabella }} + |
+
| Ripartizioni Create: | +
+ {{ $voceSpesa->ripartizioniSpese->count() }}
+ @if($voceSpesa->ripartizioniSpese->count() > 0)
+ + Ultima: {{ $voceSpesa->ripartizioniSpese->first()->created_at->format('d/m/Y') }} + + @endif + |
+
| Totale Ripartito: | ++ @php + $totaleRipartito = $voceSpesa->ripartizioniSpese->sum('importo_totale'); + @endphp + @if($totaleRipartito > 0) + € {{ number_format($totaleRipartito, 2, ',', '.') }} + @else + € 0,00 + @endif + | +
{{ $voceSpesa->descrizione }}
+{{ $voceSpesa->note }}
+| Codice | +Descrizione | +Importo | +Data | +Stato | +Azioni | +
|---|---|---|---|---|---|
{{ $ripartizione->codice_ripartizione }} |
+ {{ $ripartizione->descrizione }} | +€ {{ number_format($ripartizione->importo_totale, 2, ',', '.') }} | +{{ $ripartizione->data_ripartizione->format('d/m/Y') }} | ++ + {{ ucfirst($ripartizione->stato) }} + + | ++ + + + | +