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 } } }