netgescon-master/netgescon-laravel/app/Http/Controllers/Admin/RegistrazioniController.php

335 lines
13 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use App\Models\{
Stabile,
Gestione,
Fornitore,
VoceSpesa,
TabellaMillesimale,
MovimentoContabile,
TransazioneContabile,
RigaContabile,
PianoConti,
ProtocolloRegistrazione
};
/**
* ========================================
* CONTROLLER MASCHERA UNICA REGISTRAZIONE
* Sistema avanzato partita doppia NetGesCon
* ========================================
*/
class RegistrazioniController extends Controller
{
/**
* Mostra la maschera unica di registrazione
*/
public function create()
{
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
// Dati necessari per la maschera
$stabili = Stabile::where('amministratore_id', $amministratore_id)->attivi()->get();
$fornitori = Fornitore::where('amministratore_id', $amministratore_id)->get();
$vociSpesa = VoceSpesa::orderBy('codice')->get();
$conti = PianoConti::orderBy('codice_conto')->get();
// Template predefiniti per registrazioni comuni
$template = [
'fattura_fornitore' => [
'nome' => 'Fattura Fornitore',
'tipo_documento' => 'fattura_passiva',
'conti_automatici' => [
'dare' => ['6000' => 'Costi per servizi'],
'avere' => ['2000' => 'Debiti verso fornitori']
]
],
'bolletta_utenza' => [
'nome' => 'Bolletta Utenza',
'tipo_documento' => 'bolletta',
'conti_automatici' => [
'dare' => ['6100' => 'Spese per utenze'],
'avere' => ['2000' => 'Debiti verso fornitori']
]
],
'pagamento_bonifico' => [
'nome' => 'Pagamento Bonifico',
'tipo_documento' => 'bonifico',
'conti_automatici' => [
'dare' => ['2000' => 'Debiti verso fornitori'],
'avere' => ['1000' => 'Banca c/c']
]
],
'incasso_rata' => [
'nome' => 'Incasso Rata Condominiale',
'tipo_documento' => 'incasso',
'conti_automatici' => [
'dare' => ['1000' => 'Banca c/c'],
'avere' => ['3000' => 'Crediti verso condomini']
]
],
'ripartizione_spesa' => [
'nome' => 'Ripartizione Spesa',
'tipo_documento' => 'ripartizione',
'conti_automatici' => [
'dare' => ['3000' => 'Crediti verso condomini'],
'avere' => ['6000' => 'Costi per servizi']
]
]
];
return view('admin.registrazioni.create', compact(
'stabili', 'fornitori', 'vociSpesa', 'conti', 'template'
));
}
/**
* Salva la registrazione con logica partita doppia
*/
public function store(Request $request)
{
$request->validate([
'stabile_id' => 'required|exists:stabili,id_stabile',
'gestione_id' => 'required|exists:gestioni,id_gestione',
'tipo_documento' => 'required|string',
'numero_documento' => 'required|string',
'data_documento' => 'required|date',
'data_registrazione' => 'required|date',
'fornitore_id' => 'nullable|exists:fornitori,id_fornitore',
'descrizione' => 'required|string',
'note' => 'nullable|string',
'importo_totale' => 'required|numeric|min:0.01',
'importo_iva' => 'nullable|numeric|min:0',
'ritenuta_acconto' => 'nullable|numeric|min:0',
'righe_contabili' => 'required|array|min:2',
'righe_contabili.*.conto_id' => 'required|exists:piano_conti,id',
'righe_contabili.*.tipo_riga' => 'required|in:dare,avere',
'righe_contabili.*.importo' => 'required|numeric|min:0.01',
'righe_contabili.*.descrizione' => 'nullable|string',
'ripartizioni' => 'nullable|array',
'ripartizioni.*.tabella_millesimale_id' => 'required_with:ripartizioni|exists:tabelle_millesimali,id',
'ripartizioni.*.voce_spesa_id' => 'required_with:ripartizioni|exists:voci_spesa,id'
]);
DB::beginTransaction();
try {
// Verifica quadratura dare/avere
$totaleDare = collect($request->righe_contabili)
->where('tipo_riga', 'dare')
->sum('importo');
$totaleAvere = collect($request->righe_contabili)
->where('tipo_riga', 'avere')
->sum('importo');
if (abs($totaleDare - $totaleAvere) > 0.01) {
throw new \Exception('La registrazione non è quadrata. Dare: €' . number_format($totaleDare, 2) . ' - Avere: €' . number_format($totaleAvere, 2));
}
// Genera protocolli
$protocolloGenerale = $this->generaProtocolloGenerale($request->stabile_id);
$protocolloGestione = $this->generaProtocolloGestione($request->stabile_id, $request->gestione_id);
// Crea transazione principale
$transazione = TransazioneContabile::create([
'stabile_id' => $request->stabile_id,
'gestione_id' => $request->gestione_id,
'fornitore_id' => $request->fornitore_id,
'protocollo_generale' => $protocolloGenerale,
'protocollo_gestione' => $protocolloGestione,
'tipo_documento' => $request->tipo_documento,
'numero_documento' => $request->numero_documento,
'data_documento' => $request->data_documento,
'data_registrazione' => $request->data_registrazione,
'descrizione' => $request->descrizione,
'note' => $request->note,
'importo_totale' => $request->importo_totale,
'importo_iva' => $request->importo_iva ?? 0,
'ritenuta_acconto' => $request->ritenuta_acconto ?? 0,
'stato' => 'confermata',
'quadrata' => true,
'utente_id' => Auth::id()
]);
// Crea righe contabili
foreach ($request->righe_contabili as $riga) {
RigaContabile::create([
'transazione_id' => $transazione->id,
'conto_id' => $riga['conto_id'],
'tipo_riga' => $riga['tipo_riga'],
'importo' => $riga['importo'],
'descrizione' => $riga['descrizione'] ?? null
]);
}
// Gestione ripartizioni automatiche se presenti
if ($request->has('ripartizioni') && count($request->ripartizioni) > 0) {
$this->processaRipartizioni($transazione, $request->ripartizioni);
}
// Aggiorna saldi real-time tramite trigger SQL
DB::statement('CALL sp_aggiorna_saldi_transazione(?)', [$transazione->id]);
// Registra nel protocollo
ProtocolloRegistrazione::create([
'stabile_id' => $request->stabile_id,
'gestione_id' => $request->gestione_id,
'transazione_id' => $transazione->id,
'protocollo_generale' => $protocolloGenerale,
'protocollo_gestione' => $protocolloGestione,
'data_registrazione' => $request->data_registrazione,
'tipo_protocollo' => 'contabile',
'utente_id' => Auth::id()
]);
DB::commit();
return redirect()
->route('admin.registrazioni.show', $transazione)
->with('success', 'Registrazione contabile salvata con successo. Protocollo: ' . $protocolloGenerale);
} catch (\Exception $e) {
DB::rollback();
return back()
->withInput()
->withErrors(['error' => 'Errore durante il salvataggio: ' . $e->getMessage()]);
}
}
/**
* Mostra una registrazione
*/
public function show(TransazioneContabile $transazione)
{
$transazione->load([
'stabile',
'gestione',
'fornitore',
'righe.conto',
'ripartizioni.tabellaMillesimale',
'ripartizioni.voceSpesa',
'documentiAllegati'
]);
return view('admin.registrazioni.show', compact('transazione'));
}
/**
* API: Ottieni template per tipo documento
*/
public function getTemplate($tipoDocumento)
{
$templates = [
'fattura_passiva' => [
'righe_suggerite' => [
['conto' => '6000', 'tipo' => 'dare', 'descrizione' => 'Costo per servizi'],
['conto' => '1250', 'tipo' => 'dare', 'descrizione' => 'IVA a credito'],
['conto' => '2000', 'tipo' => 'avere', 'descrizione' => 'Debito verso fornitore']
]
],
'bonifico' => [
'righe_suggerite' => [
['conto' => '2000', 'tipo' => 'dare', 'descrizione' => 'Estinzione debito'],
['conto' => '1000', 'tipo' => 'avere', 'descrizione' => 'Uscita da banca']
]
],
'incasso' => [
'righe_suggerite' => [
['conto' => '1000', 'tipo' => 'dare', 'descrizione' => 'Entrata in banca'],
['conto' => '3000', 'tipo' => 'avere', 'descrizione' => 'Estinzione credito']
]
]
];
return response()->json($templates[$tipoDocumento] ?? []);
}
/**
* API: Calcola ripartizione automatica
*/
public function calcolaRipartizione(Request $request)
{
$request->validate([
'importo' => 'required|numeric|min:0',
'tabella_millesimale_id' => 'required|exists:tabelle_millesimali,id',
'voce_spesa_id' => 'required|exists:voci_spesa,id'
]);
$tabella = TabellaMillesimale::with('quote')->find($request->tabella_millesimale_id);
$importo = $request->importo;
$ripartizioni = [];
$totaleQuote = $tabella->quote->sum('quota_millesimi');
foreach ($tabella->quote as $quota) {
$importoQuota = round(($importo * $quota->quota_millesimi) / $totaleQuote, 2);
$ripartizioni[] = [
'condomino_id' => $quota->condomino_id,
'quota_millesimi' => $quota->quota_millesimi,
'importo' => $importoQuota,
'condomino' => $quota->condomino->ragione_sociale ?? 'N/A'
];
}
return response()->json([
'ripartizioni' => $ripartizioni,
'totale_ripartito' => array_sum(array_column($ripartizioni, 'importo')),
'tabella_nome' => $tabella->nome
]);
}
/**
* Genera protocollo generale annuale
*/
private function generaProtocolloGenerale($stabileId)
{
$anno = now()->year;
$ultimoProtocollo = ProtocolloRegistrazione::where('stabile_id', $stabileId)
->where('tipo_protocollo', 'contabile')
->whereYear('data_registrazione', $anno)
->max('protocollo_generale');
$numeroProtocollo = $ultimoProtocollo ? (intval(substr($ultimoProtocollo, -4)) + 1) : 1;
return sprintf('%d-%04d', $anno, $numeroProtocollo);
}
/**
* Genera protocollo per gestione
*/
private function generaProtocolloGestione($stabileId, $gestioneId)
{
$anno = now()->year;
$gestione = Gestione::find($gestioneId);
$ultimoProtocollo = ProtocolloRegistrazione::where('stabile_id', $stabileId)
->where('gestione_id', $gestioneId)
->where('tipo_protocollo', 'contabile')
->whereYear('data_registrazione', $anno)
->max('protocollo_gestione');
$numeroProtocollo = $ultimoProtocollo ? (intval(substr($ultimoProtocollo, -4)) + 1) : 1;
return sprintf('%s-%d-%04d', strtoupper($gestione->tipo_gestione), $anno, $numeroProtocollo);
}
/**
* Processa ripartizioni automatiche
*/
private function processaRipartizioni($transazione, $ripartizioni)
{
foreach ($ripartizioni as $ripartizione) {
// Logica per creare ripartizioni automatiche
// Implementazione dettagliata delle ripartizioni condominiali
}
}
}