🔧 Risoluzione errori migrazioni e aggiornamento DATA_ARCHITECTURE.md

- Corretti errori di foreign key nelle migrazioni
- Rimosso userSetting() non definito e aggiunto helpers.php
- Aggiornati modelli PianoRateizzazione e Rata con relazioni corrette
- Aggiornato DATA_ARCHITECTURE.md con nuovi modelli per ripartizione spese
- Corrette dipendenze tra tabelle nelle migrazioni
This commit is contained in:
Pikappa2 2025-07-09 00:47:16 +02:00
parent b0990924af
commit 2d6fba0e60
15 changed files with 705 additions and 13 deletions

View File

@ -104,6 +104,14 @@ class PianoRateizzazione extends Model
return $this->belongsTo(User::class, 'attivato_da'); return $this->belongsTo(User::class, 'attivato_da');
} }
/**
* Relazione con RipartizioneSpese
*/
public function ripartizione()
{
return $this->belongsTo(RipartizioneSpese::class, 'ripartizione_spese_id');
}
/** /**
* Relazione con Rate * Relazione con Rate
*/ */

View File

@ -105,6 +105,14 @@ class Rata extends Model
return $this->belongsTo(PianoRateizzazione::class); return $this->belongsTo(PianoRateizzazione::class);
} }
/**
* Relazione con RipartizioneSpese
*/
public function ripartizione()
{
return $this->belongsTo(RipartizioneSpese::class, 'ripartizione_spese_id');
}
/** /**
* Relazione con UnitaImmobiliare * Relazione con UnitaImmobiliare
*/ */

68
app/helpers.php Normal file
View File

@ -0,0 +1,68 @@
<?php
if (!function_exists('userSetting')) {
/**
* Ottiene un'impostazione utente
*
* @param string $key
* @param mixed $default
* @return mixed
*/
function userSetting($key, $default = null)
{
if (!auth()->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;
}
}

View File

@ -34,7 +34,8 @@
"App\\": "app/" "App\\": "app/"
}, },
"files": [ "files": [
"app/Helpers/impostazioni.php" "app/Helpers/impostazioni.php",
"app/helpers.php"
] ]
}, },
"autoload-dev": { "autoload-dev": {

View File

@ -55,15 +55,37 @@ return new class extends Migration
public function down(): void public function down(): void
{ {
Schema::table('unita_immobiliari', function (Blueprint $table) { Schema::table('unita_immobiliari', function (Blueprint $table) {
$table->dropIndex(['idx_unita_stabile_stato']); // Rimuove gli indici solo se esistono - con controllo sicuro
$table->dropIndex(['idx_unita_tipo_utilizzo']); try {
$table->dropColumn([ $table->dropIndex(['stabile_id', 'stato']);
'palazzina', 'tipo_utilizzo_id', 'millesimi_proprieta', } catch (\Exception $e) {
'rendita_catastale', 'codice_univoco', 'stato' // Indice non esistente, ignora
]); }
if (Schema::hasColumn('unita_immobiliari', 'palazzina_old')) { try {
$table->renameColumn('palazzina_old', 'fabbricato'); $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');
} }
}); });
} }

View File

@ -21,7 +21,7 @@ return new class extends Migration
$table->string('cognome')->nullable()->comment('Cognome (se persona fisica)'); $table->string('cognome')->nullable()->comment('Cognome (se persona fisica)');
$table->string('nome')->nullable()->comment('Nome (se persona fisica)'); $table->string('nome')->nullable()->comment('Nome (se persona fisica)');
$table->string('denominazione')->nullable()->comment('Denominazione (se persona giuridica)'); $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)'); $table->string('partita_iva', 11)->nullable()->comment('Partita IVA (se presente)');
// Dati nascita (solo per persone fisiche) // Dati nascita (solo per persone fisiche)

View File

@ -13,9 +13,9 @@ return new class extends Migration
{ {
Schema::create('contratti_locazione', function (Blueprint $table) { Schema::create('contratti_locazione', function (Blueprint $table) {
$table->id(); $table->id();
$table->bigInteger('unita_immobiliare_id')->unsigned()->index()->comment('FK verso unita_immobiliari'); $table->bigInteger('unita_immobiliare_id')->unsigned()->comment('FK verso unita_immobiliari');
$table->bigInteger('locatore_id')->unsigned()->index()->comment('FK verso anagrafica_condominiale (proprietario)'); $table->bigInteger('locatore_id')->unsigned()->comment('FK verso anagrafica_condominiale (proprietario)');
$table->bigInteger('conduttore_id')->unsigned()->index()->comment('FK verso anagrafica_condominiale (inquilino)'); $table->bigInteger('conduttore_id')->unsigned()->comment('FK verso anagrafica_condominiale (inquilino)');
$table->string('numero_contratto')->nullable()->comment('Numero identificativo contratto'); $table->string('numero_contratto')->nullable()->comment('Numero identificativo contratto');
$table->date('data_contratto')->comment('Data stipula contratto'); $table->date('data_contratto')->comment('Data stipula contratto');

View File

@ -13,7 +13,24 @@ return new class extends Migration
{ {
Schema::create('ripartizione_spese', function (Blueprint $table) { Schema::create('ripartizione_spese', function (Blueprint $table) {
$table->id(); $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->timestamps();
$table->softDeletes();
}); });
} }

View File

@ -13,7 +13,16 @@ return new class extends Migration
{ {
Schema::create('dettaglio_ripartizione_spese', function (Blueprint $table) { Schema::create('dettaglio_ripartizione_spese', function (Blueprint $table) {
$table->id(); $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->timestamps();
$table->softDeletes();
}); });
} }

View File

@ -13,7 +13,22 @@ return new class extends Migration
{ {
Schema::create('rate', function (Blueprint $table) { Schema::create('rate', function (Blueprint $table) {
$table->id(); $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->timestamps();
$table->softDeletes();
}); });
} }

View File

@ -13,7 +13,23 @@ return new class extends Migration
{ {
Schema::create('piano_rateizzazione', function (Blueprint $table) { Schema::create('piano_rateizzazione', function (Blueprint $table) {
$table->id(); $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->timestamps();
$table->softDeletes();
}); });
} }

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::dropIfExists('rate');
Schema::dropIfExists('piano_rateizzazione');
Schema::dropIfExists('dettaglio_ripartizione_spese');
Schema::dropIfExists('ripartizione_spese');
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Le tabelle verranno ricreate dalle migrazioni successive
}
};

View File

@ -0,0 +1,201 @@
@extends('layouts.app')
@section('title', 'Modifica Voce di Spesa')
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h3 class="card-title">
<i class="fas fa-receipt"></i> Modifica Voce di Spesa
</h3>
<div class="btn-group">
<a href="{{ route('admin.voci-spesa.show', $voceSpesa) }}" class="btn btn-secondary">
<i class="fas fa-eye"></i> Visualizza
</a>
<a href="{{ route('admin.voci-spesa.index') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Torna all'elenco
</a>
</div>
</div>
<div class="card-body">
<form method="POST" action="{{ route('admin.voci-spesa.update', $voceSpesa) }}">
@csrf
@method('PUT')
<div class="row">
<!-- Informazioni Base -->
<div class="col-md-6">
<div class="mb-3">
<label for="denominazione" class="form-label">Denominazione <span class="text-danger">*</span></label>
<input type="text" name="denominazione" id="denominazione"
class="form-control @error('denominazione') is-invalid @enderror"
value="{{ old('denominazione', $voceSpesa->denominazione) }}" required>
@error('denominazione')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="categoria" class="form-label">Categoria <span class="text-danger">*</span></label>
<select name="categoria" id="categoria" class="form-control @error('categoria') is-invalid @enderror" required>
<option value="">Seleziona categoria</option>
<option value="Amministrazione" {{ old('categoria', $voceSpesa->categoria) == 'Amministrazione' ? 'selected' : '' }}>Amministrazione</option>
<option value="Manutenzione" {{ old('categoria', $voceSpesa->categoria) == 'Manutenzione' ? 'selected' : '' }}>Manutenzione</option>
<option value="Pulizia" {{ old('categoria', $voceSpesa->categoria) == 'Pulizia' ? 'selected' : '' }}>Pulizia</option>
<option value="Utenze" {{ old('categoria', $voceSpesa->categoria) == 'Utenze' ? 'selected' : '' }}>Utenze</option>
<option value="Assicurazioni" {{ old('categoria', $voceSpesa->categoria) == 'Assicurazioni' ? 'selected' : '' }}>Assicurazioni</option>
<option value="Riscaldamento" {{ old('categoria', $voceSpesa->categoria) == 'Riscaldamento' ? 'selected' : '' }}>Riscaldamento</option>
<option value="Ascensore" {{ old('categoria', $voceSpesa->categoria) == 'Ascensore' ? 'selected' : '' }}>Ascensore</option>
<option value="Altro" {{ old('categoria', $voceSpesa->categoria) == 'Altro' ? 'selected' : '' }}>Altro</option>
</select>
@error('categoria')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="sottocategoria" class="form-label">Sottocategoria</label>
<input type="text" name="sottocategoria" id="sottocategoria"
class="form-control @error('sottocategoria') is-invalid @enderror"
value="{{ old('sottocategoria', $voceSpesa->sottocategoria) }}">
@error('sottocategoria')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
</div>
<div class="mb-3">
<label for="descrizione" class="form-label">Descrizione</label>
<textarea name="descrizione" id="descrizione" rows="3"
class="form-control @error('descrizione') is-invalid @enderror">{{ old('descrizione', $voceSpesa->descrizione) }}</textarea>
@error('descrizione')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<!-- Configurazione Ripartizione -->
<div class="col-md-6">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="importo_previsto" class="form-label">Importo Previsto ()</label>
<input type="number" name="importo_previsto" id="importo_previsto"
class="form-control @error('importo_previsto') is-invalid @enderror"
value="{{ old('importo_previsto', $voceSpesa->importo_previsto) }}" step="0.01" min="0">
@error('importo_previsto')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="periodicita" class="form-label">Periodicità</label>
<select name="periodicita" id="periodicita" class="form-control @error('periodicita') is-invalid @enderror">
<option value="">Non specificata</option>
<option value="una_tantum" {{ old('periodicita', $voceSpesa->periodicita) == 'una_tantum' ? 'selected' : '' }}>Una tantum</option>
<option value="mensile" {{ old('periodicita', $voceSpesa->periodicita) == 'mensile' ? 'selected' : '' }}>Mensile</option>
<option value="trimestrale" {{ old('periodicita', $voceSpesa->periodicita) == 'trimestrale' ? 'selected' : '' }}>Trimestrale</option>
<option value="semestrale" {{ old('periodicita', $voceSpesa->periodicita) == 'semestrale' ? 'selected' : '' }}>Semestrale</option>
<option value="annuale" {{ old('periodicita', $voceSpesa->periodicita) == 'annuale' ? 'selected' : '' }}>Annuale</option>
</select>
@error('periodicita')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
</div>
<div class="mb-3">
<label for="tabella_millesimale_default_id" class="form-label">Tabella Millesimale di Default <span class="text-danger">*</span></label>
<select name="tabella_millesimale_default_id" id="tabella_millesimale_default_id"
class="form-control @error('tabella_millesimale_default_id') is-invalid @enderror" required>
<option value="">Seleziona tabella millesimale</option>
@foreach($tabelleMillesimali as $tabella)
<option value="{{ $tabella->id }}" {{ old('tabella_millesimale_default_id', $voceSpesa->tabella_millesimale_default_id) == $tabella->id ? 'selected' : '' }}>
{{ $tabella->denominazione }} ({{ $tabella->tipo_tabella }})
</option>
@endforeach
</select>
@error('tabella_millesimale_default_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<div class="form-check">
<input type="checkbox" name="ripartizione_personalizzata" id="ripartizione_personalizzata"
class="form-check-input" value="1" {{ old('ripartizione_personalizzata', $voceSpesa->ripartizione_personalizzata) ? 'checked' : '' }}>
<label class="form-check-label" for="ripartizione_personalizzata">
Ripartizione personalizzata
</label>
</div>
<small class="form-text text-muted">
Consente di modificare manualmente la ripartizione per singola unità immobiliare
</small>
</div>
<div class="mb-3">
<label for="stato" class="form-label">Stato <span class="text-danger">*</span></label>
<select name="stato" id="stato" class="form-control @error('stato') is-invalid @enderror" required>
<option value="attiva" {{ old('stato', $voceSpesa->stato) == 'attiva' ? 'selected' : '' }}>Attiva</option>
<option value="inattiva" {{ old('stato', $voceSpesa->stato) == 'inattiva' ? 'selected' : '' }}>Inattiva</option>
<option value="archiviata" {{ old('stato', $voceSpesa->stato) == 'archiviata' ? 'selected' : '' }}>Archiviata</option>
</select>
@error('stato')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="tags" class="form-label">Tags</label>
<input type="text" name="tags" id="tags"
class="form-control @error('tags') is-invalid @enderror"
value="{{ old('tags', $voceSpesa->tags) }}" placeholder="Separati da virgola">
<small class="form-text text-muted">
Utilizzare per categorizzare e cercare più facilmente le voci di spesa
</small>
@error('tags')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="note" class="form-label">Note</label>
<textarea name="note" id="note" rows="3"
class="form-control @error('note') is-invalid @enderror">{{ old('note', $voceSpesa->note) }}</textarea>
@error('note')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
</div>
<div class="d-flex justify-content-between">
<a href="{{ route('admin.voci-spesa.show', $voceSpesa) }}" class="btn btn-secondary">
<i class="fas fa-times"></i> Annulla
</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Salva Modifiche
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,276 @@
@extends('layouts.app')
@section('title', 'Voce di Spesa - ' . $voceSpesa->denominazione)
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h3 class="card-title">
<i class="fas fa-receipt"></i> {{ $voceSpesa->denominazione }}
<span class="badge bg-{{ $voceSpesa->stato == 'attiva' ? 'success' : ($voceSpesa->stato == 'inattiva' ? 'warning' : 'secondary') }}">
{{ ucfirst($voceSpesa->stato) }}
</span>
</h3>
<div class="btn-group">
<a href="{{ route('admin.voci-spesa.edit', $voceSpesa) }}" class="btn btn-warning">
<i class="fas fa-edit"></i> Modifica
</a>
<form method="POST" action="{{ route('admin.voci-spesa.duplicate', $voceSpesa) }}" style="display: inline;">
@csrf
<button type="submit" class="btn btn-info">
<i class="fas fa-copy"></i> Duplica
</button>
</form>
<a href="{{ route('admin.voci-spesa.index') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Torna all'elenco
</a>
</div>
</div>
<div class="card-body">
<div class="row">
<!-- Informazioni Base -->
<div class="col-md-6">
<div class="card mb-3">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-info-circle"></i> Informazioni Base
</h5>
</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<td class="fw-bold">Codice:</td>
<td><code>{{ $voceSpesa->codice_spesa }}</code></td>
</tr>
<tr>
<td class="fw-bold">Stabile:</td>
<td>{{ $voceSpesa->stabile->denominazione }}</td>
</tr>
<tr>
<td class="fw-bold">Categoria:</td>
<td>
<span class="badge bg-info">{{ $voceSpesa->categoria }}</span>
@if($voceSpesa->sottocategoria)
<br><small class="text-muted">{{ $voceSpesa->sottocategoria }}</small>
@endif
</td>
</tr>
<tr>
<td class="fw-bold">Importo Previsto:</td>
<td>
@if($voceSpesa->importo_previsto)
<span class="text-success"> {{ number_format($voceSpesa->importo_previsto, 2, ',', '.') }}</span>
@else
<span class="text-muted">Non specificato</span>
@endif
</td>
</tr>
<tr>
<td class="fw-bold">Periodicità:</td>
<td>
@if($voceSpesa->periodicita)
<span class="badge bg-secondary">{{ ucfirst(str_replace('_', ' ', $voceSpesa->periodicita)) }}</span>
@else
<span class="text-muted">Non specificata</span>
@endif
</td>
</tr>
<tr>
<td class="fw-bold">Ripartizione Personalizzata:</td>
<td>
@if($voceSpesa->ripartizione_personalizzata)
<span class="badge bg-warning"></span>
@else
<span class="badge bg-success">No</span>
@endif
</td>
</tr>
</table>
</div>
</div>
</div>
<!-- Configurazione Ripartizione -->
<div class="col-md-6">
<div class="card mb-3">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-chart-pie"></i> Configurazione Ripartizione
</h5>
</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<td class="fw-bold">Tabella Millesimale Default:</td>
<td>
<strong>{{ $voceSpesa->tabellaMillesimaleDefault->denominazione }}</strong>
<br><small class="text-muted">{{ $voceSpesa->tabellaMillesimaleDefault->tipo_tabella }}</small>
</td>
</tr>
<tr>
<td class="fw-bold">Ripartizioni Create:</td>
<td>
<span class="badge bg-primary">{{ $voceSpesa->ripartizioniSpese->count() }}</span>
@if($voceSpesa->ripartizioniSpese->count() > 0)
<br><small class="text-muted">
Ultima: {{ $voceSpesa->ripartizioniSpese->first()->created_at->format('d/m/Y') }}
</small>
@endif
</td>
</tr>
<tr>
<td class="fw-bold">Totale Ripartito:</td>
<td>
@php
$totaleRipartito = $voceSpesa->ripartizioniSpese->sum('importo_totale');
@endphp
@if($totaleRipartito > 0)
<span class="text-success"> {{ number_format($totaleRipartito, 2, ',', '.') }}</span>
@else
<span class="text-muted"> 0,00</span>
@endif
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<!-- Descrizione e Note -->
@if($voceSpesa->descrizione || $voceSpesa->note || $voceSpesa->tags)
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-sticky-note"></i> Descrizione e Note
</h5>
</div>
<div class="card-body">
@if($voceSpesa->descrizione)
<div class="mb-3">
<strong>Descrizione:</strong>
<p class="mt-2">{{ $voceSpesa->descrizione }}</p>
</div>
@endif
@if($voceSpesa->tags)
<div class="mb-3">
<strong>Tags:</strong>
<div class="mt-2">
@foreach(explode(',', $voceSpesa->tags) as $tag)
<span class="badge bg-light text-dark me-1">{{ trim($tag) }}</span>
@endforeach
</div>
</div>
@endif
@if($voceSpesa->note)
<div class="mb-3">
<strong>Note:</strong>
<p class="mt-2">{{ $voceSpesa->note }}</p>
</div>
@endif
</div>
</div>
</div>
</div>
@endif
<!-- Ripartizioni Associate -->
@if($voceSpesa->ripartizioniSpese->count() > 0)
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-chart-pie"></i> Ripartizioni Associate
</h5>
<a href="{{ route('admin.ripartizioni-spesa.create', ['voce_spesa_id' => $voceSpesa->id]) }}" class="btn btn-primary btn-sm">
<i class="fas fa-plus"></i> Nuova Ripartizione
</a>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Codice</th>
<th>Descrizione</th>
<th>Importo</th>
<th>Data</th>
<th>Stato</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
@foreach($voceSpesa->ripartizioniSpese as $ripartizione)
<tr>
<td><code>{{ $ripartizione->codice_ripartizione }}</code></td>
<td>{{ $ripartizione->descrizione }}</td>
<td> {{ number_format($ripartizione->importo_totale, 2, ',', '.') }}</td>
<td>{{ $ripartizione->data_ripartizione->format('d/m/Y') }}</td>
<td>
<span class="badge bg-{{ $ripartizione->stato == 'confermata' ? 'success' : 'warning' }}">
{{ ucfirst($ripartizione->stato) }}
</span>
</td>
<td>
<a href="{{ route('admin.ripartizioni-spesa.show', $ripartizione) }}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-eye"></i>
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
@endif
<!-- Informazioni Sistema -->
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-info-circle"></i> Informazioni Sistema
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<small class="text-muted">
<strong>Creato:</strong> {{ $voceSpesa->created_at->format('d/m/Y H:i:s') }}
@if($voceSpesa->createdBy)
da {{ $voceSpesa->createdBy->name }}
@endif
</small>
</div>
<div class="col-md-6">
<small class="text-muted">
<strong>Ultimo aggiornamento:</strong> {{ $voceSpesa->updated_at->format('d/m/Y H:i:s') }}
@if($voceSpesa->updatedBy)
da {{ $voceSpesa->updatedBy->name }}
@endif
</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -26,6 +26,30 @@
'route' => $panelPrefix . 'soggetti.index', 'route' => $panelPrefix . 'soggetti.index',
'roles' => ['admin', 'super-admin', 'amministratore', 'collaboratore'], 'roles' => ['admin', 'super-admin', 'amministratore', 'collaboratore'],
], ],
[
'icon' => 'fa-solid fa-receipt',
'label' => 'Voci di Spesa',
'route' => $panelPrefix . 'voci-spesa.index',
'roles' => ['admin', 'super-admin', 'amministratore'],
],
[
'icon' => 'fa-solid fa-chart-pie',
'label' => 'Ripartizioni Spesa',
'route' => $panelPrefix . 'ripartizioni-spesa.index',
'roles' => ['admin', 'super-admin', 'amministratore'],
],
[
'icon' => 'fa-solid fa-credit-card',
'label' => 'Piani Rateizzazione',
'route' => $panelPrefix . 'piani-rateizzazione.index',
'roles' => ['admin', 'super-admin', 'amministratore'],
],
[
'icon' => 'fa-solid fa-calendar-check',
'label' => 'Rate',
'route' => $panelPrefix . 'rate.index',
'roles' => ['admin', 'super-admin', 'amministratore'],
],
[ [
'icon' => 'fa-solid fa-file-invoice-dollar', 'icon' => 'fa-solid fa-file-invoice-dollar',
'label' => __('menu.contabilita'), 'label' => __('menu.contabilita'),