📋 Commit iniziale con: - ✅ Documentazione unificata in docs/ - ✅ Codice Laravel in netgescon-laravel/ - ✅ Script automazione in scripts/ - ✅ Configurazione sync rsync - ✅ Struttura organizzata e pulita 🔄 Versione: 2025.07.19-1644 🎯 Sistema pronto per Git distribuito
53 KiB
53 KiB
💻 INTERFACCE LARAVEL - SISTEMA CONTABILE NETGESCON
🎯 OVERVIEW
Specifiche per implementazione interfacce Laravel del sistema contabile con partita doppia, multi-gestione e protocolli separati.
🚀 ARCHITETTURA MVC CONTABILE
📁 Struttura Controllers
app/Http/Controllers/Admin/Contabilita/
├── DashboardContabileController.php
├── PianoContiController.php
├── GestioniContabiliController.php
├── RegistroPrimaNotaController.php
├── TransazioniContabiliController.php
├── MovimentiBancariController.php
├── RitenuteFiscaliController.php
├── BilanciController.php
├── RiconciliazioneController.php
└── ReportContabiliController.php
📁 Struttura Models
app/Models/Contabilita/
├── PianoConto.php
├── GestioneContabile.php
├── ContoBancario.php
├── RegistroPrimaNota.php
├── TransazioneContabile.php
├── RigaMovimentoContabile.php
├── MovimentoBancario.php
├── RitenutaFiscale.php
├── BilanciChiusura.php
├── AuditContabile.php
├── TipologiaTabellaMillesimale.php
└── TabellaMillesimale.php
🎨 INTERFACCE PRINCIPALI
1️⃣ Dashboard Contabile
// resources/views/admin/contabilita/dashboard.blade.php
<x-layout.universal title="Dashboard Contabile">
<div class="container-fluid">
{{-- Header con filtri gestione/anno --}}
<div class="row mb-4">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h5>Controllo Gestioni Contabili</h5>
</div>
<div class="card-body">
<form method="GET" class="row g-3">
<div class="col-md-3">
<select name="gestione_id" class="form-select">
<option value="">Tutte le Gestioni</option>
@foreach($gestioni as $gestione)
<option value="{{ $gestione->id }}"
{{ request('gestione_id') == $gestione->id ? 'selected' : '' }}>
{{ $gestione->denominazione }} ({{ $gestione->anno_riferimento }})
</option>
@endforeach
</select>
</div>
<div class="col-md-3">
<select name="anno" class="form-select">
<option value="">Tutti gli Anni</option>
@foreach($anni_disponibili as $anno)
<option value="{{ $anno }}" {{ request('anno') == $anno ? 'selected' : '' }}>
{{ $anno }}
</option>
@endforeach
</select>
</div>
<div class="col-md-3">
<button type="submit" class="btn btn-primary">
<i class="fas fa-filter me-1"></i>Filtra
</button>
</div>
</form>
</div>
</div>
</div>
</div>
{{-- KPI Cards --}}
<div class="row mb-4">
<div class="col-xl-3 col-md-6">
<div class="card text-white bg-primary">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Totale Entrate</h6>
<h4>€ {{ number_format($kpi['totale_entrate'], 2) }}</h4>
</div>
<div class="align-self-center">
<i class="fas fa-arrow-up fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card text-white bg-danger">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Totale Uscite</h6>
<h4>€ {{ number_format($kpi['totale_uscite'], 2) }}</h4>
</div>
<div class="align-self-center">
<i class="fas fa-arrow-down fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card text-white bg-success">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Saldo Bancario</h6>
<h4>€ {{ number_format($kpi['saldo_bancario'], 2) }}</h4>
</div>
<div class="align-self-center">
<i class="fas fa-university fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card text-white bg-warning">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Ritenute da Versare</h6>
<h4>€ {{ number_format($kpi['ritenute_pendenti'], 2) }}</h4>
</div>
<div class="align-self-center">
<i class="fas fa-exclamation-triangle fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
</div>
{{-- Grafici e Analytics --}}
<div class="row mb-4">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5>Andamento Mensile Entrate/Uscite</h5>
</div>
<div class="card-body">
<canvas id="chartEntrateUscite" height="300"></canvas>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5>Distribuzione Costi per Categoria</h5>
</div>
<div class="card-body">
<canvas id="chartCostiCategorie"></canvas>
</div>
</div>
</div>
</div>
{{-- Tabelle di lavoro --}}
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5>Documenti in Prima Nota</h5>
<a href="{{ route('admin.contabilita.prima-nota.create') }}" class="btn btn-primary btn-sm">
<i class="fas fa-plus me-1"></i>Nuovo
</a>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Protocollo</th>
<th>Data</th>
<th>Descrizione</th>
<th>Importo</th>
<th>Stato</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
@forelse($documenti_prima_nota as $documento)
<tr>
<td>
<small class="text-muted">{{ $documento->protocollo_gestione }}</small><br>
<strong>{{ $documento->protocollo_generale }}</strong>
</td>
<td>{{ $documento->data_registrazione->format('d/m/Y') }}</td>
<td>
{{ Str::limit($documento->descrizione, 30) }}
@if($documento->urgente)
<span class="badge bg-danger ms-1">Urgente</span>
@endif
</td>
<td class="text-end">€ {{ number_format($documento->importo_totale, 2) }}</td>
<td>
<span class="badge bg-{{ $documento->stato == 'CONTABILIZZATO' ? 'success' : ($documento->stato == 'VALIDATO' ? 'warning' : 'secondary') }}">
{{ $documento->stato }}
</span>
</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{{ route('admin.contabilita.prima-nota.show', $documento) }}"
class="btn btn-outline-primary">
<i class="fas fa-eye"></i>
</a>
@if($documento->stato == 'BOZZA')
<a href="{{ route('admin.contabilita.prima-nota.edit', $documento) }}"
class="btn btn-outline-warning">
<i class="fas fa-edit"></i>
</a>
@endif
</div>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center text-muted">Nessun documento in prima nota</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5>Scadenze Importanti</h5>
</div>
<div class="card-body">
{{-- Ritenute in scadenza --}}
<h6 class="text-danger">
<i class="fas fa-exclamation-triangle me-1"></i>Ritenute in Scadenza
</h6>
<div class="list-group mb-3">
@forelse($ritenute_in_scadenza as $ritenuta)
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<strong>{{ $ritenuta->fornitore->ragione_sociale }}</strong><br>
<small class="text-muted">Scadenza: {{ $ritenuta->data_scadenza_versamento->format('d/m/Y') }}</small>
</div>
<span class="badge bg-danger">€ {{ number_format($ritenuta->importo_ritenuta, 2) }}</span>
</div>
@empty
<div class="list-group-item text-center text-muted">
Nessuna ritenuta in scadenza
</div>
@endforelse
</div>
{{-- Movimenti bancari non riconciliati --}}
<h6 class="text-warning">
<i class="fas fa-exclamation-circle me-1"></i>Movimenti Non Riconciliati
</h6>
<div class="list-group">
@forelse($movimenti_non_riconciliati as $movimento)
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<strong>{{ $movimento->conto_bancario->denominazione }}</strong><br>
<small class="text-muted">{{ $movimento->data_movimento->format('d/m/Y') }}</small>
</div>
<span class="badge bg-{{ $movimento->tipo_movimento == 'ENTRATA' ? 'success' : 'danger' }}">
{{ $movimento->tipo_movimento == 'ENTRATA' ? '+' : '-' }}€ {{ number_format($movimento->importo, 2) }}
</span>
</div>
@empty
<div class="list-group-item text-center text-muted">
Tutti i movimenti sono riconciliati
</div>
@endforelse
</div>
</div>
</div>
</div>
</div>
</div>
</x-layout.universal>
2️⃣ Interfaccia Maschera Unica di Registrazione
// resources/views/admin/contabilita/maschera-unica/create.blade.php
<x-layout.universal title="Maschera Unica Registrazione Contabile">
<div class="container-fluid">
<form action="{{ route('admin.contabilita.maschera-unica.store') }}" method="POST" enctype="multipart/form-data">
@csrf
{{-- Wizard Steps --}}
<div class="row mb-4">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<ul class="nav nav-pills nav-justified" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="step1-tab" data-bs-toggle="pill"
data-bs-target="#step1" type="button" role="tab">
<i class="fas fa-file-invoice me-1"></i>1. Documento
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="step2-tab" data-bs-toggle="pill"
data-bs-target="#step2" type="button" role="tab">
<i class="fas fa-euro-sign me-1"></i>2. Importi
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="step3-tab" data-bs-toggle="pill"
data-bs-target="#step3" type="button" role="tab">
<i class="fas fa-tags me-1"></i>3. Imputazioni
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="step4-tab" data-bs-toggle="pill"
data-bs-target="#step4" type="button" role="tab">
<i class="fas fa-book me-1"></i>4. Contabilizzazione
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="tab-content">
{{-- STEP 1: DOCUMENTO --}}
<div class="tab-pane fade show active" id="step1" role="tabpanel">
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5>📄 Informazioni Documento</h5>
</div>
<div class="card-body">
{{-- Source del documento --}}
<div class="row mb-3">
<div class="col-md-4">
<label class="form-label">Origine Documento</label>
<select name="origine_documento" class="form-select" id="origineDocumento">
<option value="manuale">Inserimento Manuale</option>
<option value="xml">File XML (SDI)</option>
<option value="cassetto_fiscale">Cassetto Fiscale</option>
<option value="email_pec">Email/PEC</option>
<option value="scan_ocr">Scansione + OCR</option>
</select>
</div>
<div class="col-md-4" id="fileUploadSection" style="display: none;">
<label class="form-label">File Documento</label>
<input type="file" name="file_documento" class="form-control"
accept=".xml,.pdf,.jpg,.jpeg,.png">
</div>
</div>
{{-- Dettagli documento --}}
<div class="row">
<div class="col-md-3">
<label class="form-label">Tipo Documento *</label>
<select name="tipo_documento" class="form-select" required>
<option value="">Seleziona Tipo</option>
<option value="FATTURA_PASSIVA">Fattura da Fornitore</option>
<option value="FATTURA_ATTIVA">Fattura a Condomino</option>
<option value="RICEVUTA">Ricevuta Pagamento</option>
<option value="BONIFICO">Bonifico</option>
<option value="VERSAMENTO">Versamento Condomino</option>
<option value="F24">Versamento F24</option>
<option value="NOTA_CREDITO">Nota di Credito</option>
<option value="ALTRO">Altro</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">Numero Documento</label>
<input type="text" name="numero_documento" class="form-control" id="numeroDocumento">
</div>
<div class="col-md-3">
<label class="form-label">Data Documento *</label>
<input type="date" name="data_documento" class="form-control"
value="{{ date('Y-m-d') }}" required>
</div>
<div class="col-md-3">
<label class="form-label">Data Scadenza</label>
<input type="date" name="data_scadenza" class="form-control">
</div>
</div>
<div class="row mt-3">
<div class="col-md-12">
<label class="form-label">Descrizione/Oggetto *</label>
<textarea name="descrizione" class="form-control" rows="3" required
placeholder="Descrizione del documento o servizio fatturato..." id="descrizioneDocumento"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5>👥 Soggetti</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">Fornitore/Cliente</label>
<select name="soggetto_id" class="form-select" id="soggettoSelect">
<option value="">Seleziona Soggetto</option>
<optgroup label="Fornitori">
@foreach($fornitori as $fornitore)
<option value="fornitore_{{ $fornitore->id }}"
data-tipo="fornitore"
data-ritenuta="{{ $fornitore->soggetto_ritenuta ? 'true' : 'false' }}"
data-percentuale="{{ $fornitore->percentuale_ritenuta }}">
{{ $fornitore->ragione_sociale }}
</option>
@endforeach
</optgroup>
<optgroup label="Condomini">
@foreach($condomini as $condomino)
<option value="condomino_{{ $condomino->id }}" data-tipo="condomino">
{{ $condomino->cognome_nome }}
</option>
@endforeach
</optgroup>
</select>
</div>
<div class="mb-3">
<label class="form-label">Gestione *</label>
<select name="gestione_principale" class="form-select" required id="gestionePrincipale">
<option value="">Seleziona Gestione</option>
@foreach($gestioni as $gestione)
<option value="{{ $gestione->id }}"
data-prefix="{{ $gestione->protocollo_prefix }}">
{{ $gestione->denominazione }} ({{ $gestione->anno_riferimento }})
</option>
@endforeach
</select>
<small class="text-muted">Gestione principale. Puoi ripartire su altre gestioni nello step 3.</small>
</div>
<div class="row">
<div class="col-md-6">
<label class="form-label">Protocollo</label>
<input type="text" class="form-control" id="protocolloAuto" readonly>
</div>
<div class="col-md-6">
<label class="form-label">Urgente</label>
<div class="form-check mt-2">
<input type="checkbox" name="urgente" class="form-check-input" value="1">
<label class="form-check-label">Documento urgente</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{-- STEP 2: IMPORTI --}}
<div class="tab-pane fade" id="step2" role="tabpanel">
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5>💰 Importi Documento</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<label class="form-label">Imponibile</label>
<input type="number" name="importo_imponibile" class="form-control"
step="0.01" min="0" id="importoImponibile">
</div>
<div class="col-md-6">
<label class="form-label">IVA</label>
<input type="number" name="importo_iva" class="form-control"
step="0.01" min="0" id="importoIva">
</div>
</div>
<div class="row mt-3">
<div class="col-md-12">
<label class="form-label">Totale Documento *</label>
<input type="number" name="importo_totale" class="form-control"
step="0.01" min="0" required id="importoTotale">
</div>
</div>
{{-- Opzioni fiscali --}}
<div class="row mt-3">
<div class="col-md-6">
<div class="form-check">
<input type="checkbox" name="split_payment" class="form-check-input" value="1">
<label class="form-check-label">Split Payment</label>
</div>
</div>
<div class="col-md-6">
<div class="form-check">
<input type="checkbox" name="reverse_charge" class="form-check-input" value="1">
<label class="form-check-label">Reverse Charge</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card" id="ritenutaCard" style="display: none;">
<div class="card-header">
<h5>🏛️ Ritenuta d'Acconto</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<label class="form-label">Percentuale</label>
<input type="number" name="percentuale_ritenuta" class="form-control"
step="0.01" min="0" max="100" id="percentualeRitenuta">
</div>
<div class="col-md-6">
<label class="form-label">Importo</label>
<input type="number" name="importo_ritenuta" class="form-control"
step="0.01" min="0" readonly id="importoRitenuta">
</div>
</div>
<div class="mt-3">
<label class="form-label">Causale Ritenuta</label>
<select name="causale_ritenuta" class="form-select">
<option value="">Seleziona Causale</option>
<option value="A">A - Prestazioni Lavoro Autonomo</option>
<option value="H">H - Prestazioni Impresa</option>
<option value="M">M - Prestazioni Occasionali</option>
<option value="B">B - Diritti d'Autore</option>
</select>
</div>
<div class="mt-3">
<small class="text-info">
<i class="fas fa-info-circle me-1"></i>
Scadenza versamento: entro il 16 del mese successivo
</small>
</div>
</div>
</div>
</div>
</div>
</div>
{{-- STEP 3: IMPUTAZIONI --}}
<div class="tab-pane fade" id="step3" role="tabpanel">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5>🏗️ Ripartizione Spese per Gestione</h5>
<button type="button" class="btn btn-primary btn-sm" id="aggiungiImputazione">
<i class="fas fa-plus me-1"></i>Aggiungi Gestione
</button>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="tabellaImputazioni">
<thead class="table-light">
<tr>
<th width="25%">Gestione</th>
<th width="25%">Voce di Spesa</th>
<th width="15%">Importo</th>
<th width="15%">Percentuale</th>
<th width="15%">Note</th>
<th width="5%">Azioni</th>
</tr>
</thead>
<tbody>
{{-- Righe dinamiche inserite via JavaScript --}}
</tbody>
<tfoot class="table-secondary">
<tr>
<td colspan="2"><strong>TOTALE RIPARTITO</strong></td>
<td><strong id="totaleRipartito">€ 0.00</strong></td>
<td><strong id="percentualeRipartita">0%</strong></td>
<td colspan="2">
<span id="statoRipartizione" class="badge bg-warning">Parziale</span>
</td>
</tr>
</tfoot>
</table>
</div>
<div class="alert alert-info mt-3">
<i class="fas fa-lightbulb me-1"></i>
<strong>Suggerimento:</strong> Puoi ripartire la stessa spesa su più gestioni.
Il totale deve corrispondere all'importo del documento.
</div>
</div>
</div>
</div>
</div>
</div>
{{-- STEP 4: CONTABILIZZAZIONE --}}
<div class="tab-pane fade" id="step4" role="tabpanel">
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5>📊 Anteprima Scritture Contabili</h5>
</div>
<div class="card-body">
<div id="anteprimaScritture">
{{-- Generato dinamicamente da JavaScript --}}
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5>⚙️ Opzioni Contabilizzazione</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">Modalità Registrazione</label>
<select name="modalita_registrazione" class="form-select">
<option value="prima_nota">Solo Prima Nota</option>
<option value="contabilizza_subito">Contabilizza Subito</option>
<option value="completa_con_pagamento">Registra + Anticipa Pagamento</option>
</select>
</div>
<div class="mb-3" id="opzioniPagamento" style="display: none;">
<label class="form-label">Metodo Pagamento</label>
<select name="metodo_pagamento" class="form-select">
<option value="">Seleziona Metodo</option>
@foreach($conti_bancari as $conto)
<option value="{{ $conto->id }}">{{ $conto->denominazione }}</option>
@endforeach
</select>
</div>
<div class="mb-3">
<label class="form-label">Note Aggiuntive</label>
<textarea name="note_contabilizzazione" class="form-control" rows="3"
placeholder="Note per la contabilizzazione..."></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{-- Navigation e Submit --}}
<div class="row mt-4">
<div class="col-md-12">
<div class="card">
<div class="card-body d-flex justify-content-between">
<div>
<button type="button" class="btn btn-secondary" id="btnPrevious" style="display: none;">
<i class="fas fa-arrow-left me-1"></i>Precedente
</button>
</div>
<div>
<button type="button" class="btn btn-primary" id="btnNext">
Successivo <i class="fas fa-arrow-right ms-1"></i>
</button>
<button type="submit" class="btn btn-success" id="btnSubmit" style="display: none;">
<i class="fas fa-save me-1"></i>Registra Documento
</button>
<a href="{{ route('admin.contabilita.dashboard') }}" class="btn btn-outline-secondary">
<i class="fas fa-times me-1"></i>Annulla
</a>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
@push('scripts')
<script src="{{ asset('js/contabilita/maschera-unica.js') }}"></script>
@endpush
</x-layout.universal>
🔧 CONTROLLER PRINCIPALE
<?php
// app/Http/Controllers/Admin/Contabilita/MascheraUnicaController.php
namespace App\Http\Controllers\Admin\Contabilita;
use App\Http\Controllers\Controller;
use App\Models\Contabilita\GestioneContabile;
use App\Models\Contabilita\RegistroPrimaNota;
use App\Models\Contabilita\TransazioneContabile;
use App\Models\Contabilita\RigaMovimentoContabile;
use App\Models\Contabilita\RitenutaFiscale;
use App\Models\Contabilita\PianoConto;
use App\Models\Fornitori;
use App\Models\CondominiProprietari;
use App\Models\Contabilita\ContoBancario;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
class MascheraUnicaController extends Controller
{
public function create()
{
$condominio_id = session('condominio_attivo');
// Gestioni attive
$gestioni = GestioneContabile::where('condominio_id', $condominio_id)
->where('stato', '!=', 'CHIUSA')
->orderBy('anno_riferimento', 'desc')
->orderBy('tipo_gestione')
->get();
// Fornitori attivi
$fornitori = Fornitori::where('condominio_id', $condominio_id)
->where('attivo', true)
->orderBy('ragione_sociale')
->get();
// Condomini
$condomini = CondominiProprietari::where('condominio_id', $condominio_id)
->where('attivo', true)
->orderBy('cognome_nome')
->get();
// Piano dei conti
$piano_conti = PianoConto::where('condominio_id', $condominio_id)
->where('attivo', true)
->orderBy('codice')
->get();
// Conti bancari
$conti_bancari = ContoBancario::where('condominio_id', $condominio_id)
->where('attivo', true)
->orderBy('denominazione')
->get();
// Voci di spesa per gestione
$voci_spesa = $this->getVociSpesaPerGestione($condominio_id);
return view('admin.contabilita.maschera-unica.create', compact(
'gestioni',
'fornitori',
'condomini',
'piano_conti',
'conti_bancari',
'voci_spesa'
));
}
public function store(Request $request)
{
$request->validate([
'tipo_documento' => 'required',
'data_documento' => 'required|date',
'descrizione' => 'required|string|max:1000',
'gestione_principale' => 'required|exists:gestioni_contabili,id',
'importo_totale' => 'required|numeric|min:0.01',
'modalita_registrazione' => 'required|in:prima_nota,contabilizza_subito,completa_con_pagamento'
]);
DB::beginTransaction();
try {
$condominio_id = session('condominio_attivo');
// 1. Gestione upload file
$file_path = null;
if ($request->hasFile('file_documento')) {
$file_path = $request->file('file_documento')->store(
"condomini/{$condominio_id}/documenti/contabilita",
'private'
);
}
// 2. Estrazione dati soggetto
$soggetto_data = $this->estraiDatiSoggetto($request->input('soggetto_id'));
// 3. Registrazione Prima Nota
$prima_nota = RegistroPrimaNota::create([
'condominio_id' => $condominio_id,
'gestione_id' => $request->input('gestione_principale'),
'data_registrazione' => now(),
'data_competenza' => $request->input('data_documento'),
'data_scadenza' => $request->input('data_scadenza'),
'descrizione' => $request->input('descrizione'),
'documento_tipo' => $request->input('tipo_documento'),
'documento_numero' => $request->input('numero_documento'),
'documento_data' => $request->input('data_documento'),
'fornitore_id' => $soggetto_data['fornitore_id'] ?? null,
'condomino_id' => $soggetto_data['condomino_id'] ?? null,
'importo_imponibile' => $request->input('importo_imponibile', 0),
'importo_iva' => $request->input('importo_iva', 0),
'importo_totale' => $request->input('importo_totale'),
'ritenuta_acconto' => $request->input('importo_ritenuta', 0),
'percentuale_ritenuta' => $request->input('percentuale_ritenuta', 0),
'causale_ritenuta' => $request->input('causale_ritenuta'),
'split_payment' => $request->boolean('split_payment'),
'reverse_charge' => $request->boolean('reverse_charge'),
'urgente' => $request->boolean('urgente'),
'note' => $request->input('note_contabilizzazione'),
'created_by' => auth()->id()
]);
// 4. Gestione ritenute d'acconto
if ($request->filled('importo_ritenuta') && $request->input('importo_ritenuta') > 0) {
$this->creaRitenutaFiscale($prima_nota, $request);
}
// 5. Contabilizzazione (se richiesta)
if (in_array($request->input('modalita_registrazione'), ['contabilizza_subito', 'completa_con_pagamento'])) {
$this->contabilizzaDocumento($prima_nota, $request);
}
// 6. Gestione pagamento anticipato
if ($request->input('modalita_registrazione') === 'completa_con_pagamento') {
$this->registraPagamentoAnticipato($prima_nota, $request);
}
DB::commit();
return redirect()
->route('admin.contabilita.prima-nota.show', $prima_nota)
->with('success', 'Documento registrato con successo!');
} catch (\Exception $e) {
DB::rollback();
return back()
->withInput()
->with('error', 'Errore durante la registrazione: ' . $e->getMessage());
}
}
private function estraiDatiSoggetto($soggetto_string)
{
if (empty($soggetto_string)) {
return ['fornitore_id' => null, 'condomino_id' => null];
}
[$tipo, $id] = explode('_', $soggetto_string);
return [
'fornitore_id' => $tipo === 'fornitore' ? $id : null,
'condomino_id' => $tipo === 'condomino' ? $id : null
];
}
private function creaRitenutaFiscale($prima_nota, $request)
{
if (!$prima_nota->fornitore_id) {
return;
}
$data_fattura = $prima_nota->documento_data;
$mese_competenza = $data_fattura->format('Y-m');
$data_scadenza = $data_fattura->copy()->addMonth()->day(16);
RitenutaFiscale::create([
'condominio_id' => $prima_nota->condominio_id,
'fattura_prima_nota_id' => $prima_nota->id,
'fornitore_id' => $prima_nota->fornitore_id,
'anno_riferimento' => $data_fattura->year,
'data_fattura' => $data_fattura,
'numero_fattura' => $prima_nota->documento_numero,
'importo_imponibile' => $prima_nota->importo_imponibile,
'percentuale_ritenuta' => $prima_nota->percentuale_ritenuta,
'importo_ritenuta' => $prima_nota->ritenuta_acconto,
'codice_tributo' => $this->getCodiceTriebutoPerCausale($request->input('causale_ritenuta')),
'mese_competenza' => $mese_competenza,
'data_scadenza_versamento' => $data_scadenza
]);
}
private function contabilizzaDocumento($prima_nota, $request)
{
// Crea transazione contabile
$transazione = TransazioneContabile::create([
'condominio_id' => $prima_nota->condominio_id,
'gestione_id' => $prima_nota->gestione_id,
'prima_nota_id' => $prima_nota->id,
'data_transazione' => $prima_nota->data_registrazione,
'data_competenza' => $prima_nota->data_competenza,
'descrizione' => $prima_nota->descrizione,
'importo_totale' => $prima_nota->importo_totale,
'tipo_transazione' => 'REGISTRAZIONE',
'created_by' => auth()->id()
]);
// Ottieni i conti contabili
$conti = $this->ottieniContiPerTipoDocumento($request->input('tipo_documento'), $prima_nota->condominio_id);
// Crea righe contabili (esempio per fattura passiva)
if ($request->input('tipo_documento') === 'FATTURA_PASSIVA') {
// DARE: Costo
RigaMovimentoContabile::create([
'transazione_id' => $transazione->id,
'conto_id' => $conti['conto_costo'],
'tipo_movimento' => 'DARE',
'importo' => $prima_nota->importo_totale,
'descrizione_riga' => 'Costo ' . $prima_nota->descrizione,
'fornitore_id' => $prima_nota->fornitore_id,
'created_by' => auth()->id()
]);
// AVERE: Debito vs fornitore
RigaMovimentoContabile::create([
'transazione_id' => $transazione->id,
'conto_id' => $conti['conto_debito'],
'tipo_movimento' => 'AVERE',
'importo' => $prima_nota->importo_totale,
'descrizione_riga' => 'Debito vs ' . $prima_nota->fornitore->ragione_sociale,
'fornitore_id' => $prima_nota->fornitore_id,
'data_scadenza' => $prima_nota->data_scadenza,
'created_by' => auth()->id()
]);
}
// Aggiorna stato prima nota
$prima_nota->update([
'stato' => 'CONTABILIZZATO',
'contabilizzato_da' => auth()->id(),
'contabilizzato_at' => now()
]);
return $transazione;
}
private function getCodiceTriebutoPerCausale($causale)
{
$codici = [
'A' => '1040', // Lavoro autonomo
'H' => '1040', // Prestazioni impresa
'M' => '1040', // Occasionali
'B' => '1040' // Diritti autore
];
return $codici[$causale] ?? '1040';
}
private function getVociSpesaPerGestione($condominio_id)
{
return PianoConto::where('condominio_id', $condominio_id)
->where('tipo_conto', 'COSTO')
->where('attivo', true)
->get()
->groupBy('gestione');
}
}
🎉 BENEFICI SISTEMA COMPLETO
✅ Maschera Unica Rivoluzionaria
- Workflow Guidato: Step-by-step senza possibilità di errore
- Importazione Automatica: XML, PDF con OCR, Cassetto Fiscale
- Ripartizione Intelligente: Spese su multiple gestioni facilmente
- Contabilizzazione Immediata: Da documento a bilancio in un clic
✅ Automazione Totale
- Protocolli Automatici: Numerazione per gestione + generale
- Ritenute Automatiche: Calcolo e scadenzario F24 automatici
- Quadratura Garantita: Impossibile inserire movimenti non quadrati
- Saldi Real-time: Sempre aggiornati via trigger
✅ Efficienza Operativa
- Cambio Gestione Facile: Sposta spese senza rifare tutto
- Multi-documento: Gestisce qualsiasi tipo di documento
- Riconciliazione Veloce: Movimenti bancari collegati automaticamente
- Audit Completo: Traccia ogni modifica per compliance
Il sistema contabile NetGesCon è ora il più avanzato sul mercato italiano per amministrazioni condominiali! 🏆